@agent-native/core 0.169.1 → 0.170.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-native.eject.json +4 -1
- package/bin/agent-native.js +23 -5
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
- package/corpus/templates/analytics/app/i18n-data.ts +28 -0
- package/corpus/templates/analytics/app/pages/Agents.tsx +25 -1
- package/corpus/templates/analytics/app/routes/$.tsx +10 -0
- package/corpus/templates/analytics/app/routes.ts +3 -1
- package/corpus/templates/analytics/changelog/2026-08-22-fixed-the-agent-native-templates-first-party-dashboard-panel.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-22-org-admin-panels-no-longer-report-a-database-error-as-a-per.md +9 -0
- package/corpus/templates/analytics/changelog/2026-08-22-slack-requests-report-invalid-inputs.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-22-unknown-analytics-links-now-return-a-real-not-found-status.md +6 -0
- package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +2 -1
- package/corpus/templates/analytics/docs/schemas/tracked-events.md +2 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +29 -7
- package/corpus/templates/analytics/server/handlers/slack.ts +59 -5
- package/corpus/templates/analytics/server/lib/db-admin-connections.ts +11 -6
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +132 -2
- package/corpus/templates/analytics/server/lib/slack.ts +4 -2
- package/corpus/templates/analytics/vitest.config.ts +6 -1
- package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/assets/app/routes/library.tsx +5 -3
- package/corpus/templates/assets/app/routes.ts +3 -1
- package/corpus/templates/assets/changelog/2026-08-22-library-page-loads-again-instead-of-crashing-on-open.md +6 -0
- package/corpus/templates/assets/vitest.config.ts +6 -1
- package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/brain/app/components/ui/pointer-lock.ts +1 -0
- package/corpus/templates/brain/app/routes/sources.tsx +8 -3
- package/corpus/templates/brain/changelog/2026-08-22-tuning-or-adding-a-source-no-longer-leaves-the-sources-page.md +6 -0
- package/corpus/templates/brain/vitest.config.ts +1 -0
- package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/calendar/actions/list-booking-links.ts +83 -4
- package/corpus/templates/calendar/actions/update-booking-link.ts +4 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +11 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +1 -1
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +103 -44
- package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +28 -24
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +9 -2
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +47 -58
- package/corpus/templates/calendar/app/hooks/use-booking-links.ts +5 -1
- package/corpus/templates/calendar/app/lib/event-layout.ts +52 -26
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +126 -98
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +1 -0
- package/corpus/templates/calendar/app/pages/Settings.tsx +88 -81
- package/corpus/templates/calendar/app/routes.ts +3 -1
- package/corpus/templates/calendar/changelog/2026-08-22-booking-link-visibility-respects-shared-access.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-clicking-an-out-of-office-event-s-marker-no-longer-creates-a.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-event-detail-views-now-show-your-calendar-timezone.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-google-calendar-can-now-connect-with-the-shared-google-oauth.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-overlapping-events-now-show-each-others-boundaries.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-public-booking-pages-now-offer-time-slots-from-the-host-s-we.md +6 -0
- package/corpus/templates/calendar/server/handlers/bookings.ts +5 -7
- package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -2
- package/corpus/templates/calendar/server/lib/google-calendar.ts +139 -14
- package/corpus/templates/calendar/server/onboarding.ts +85 -0
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/calendar/shared/api.ts +9 -1
- package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/chat/app/routes.ts +3 -1
- package/corpus/templates/chat/vitest.config.ts +8 -0
- package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/clips/actions/get-meeting.ts +9 -4
- package/corpus/templates/clips/actions/list-recordings.ts +16 -3
- package/corpus/templates/clips/actions/search-meetings.ts +1 -0
- package/corpus/templates/clips/actions/view-screen.ts +8 -1
- package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +32 -1
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +29 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +5 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +33 -4
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -0
- package/corpus/templates/clips/app/routes/access-request.approve.tsx +16 -9
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +17 -7
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +17 -7
- package/corpus/templates/clips/changelog/2026-08-21-camera-bubble-black-circle.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-22-screen-recordings-respect-the-microphone-toggle.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-22-the-recordings-library-no-longer-fails-right-after-server-start.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-23-the-agent-can-see-your-library-right-after-server-start.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +1 -2
- package/corpus/templates/clips/chrome-extension/src/native-transcription.ts +39 -7
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +21 -25
- package/corpus/templates/clips/desktop/src/app.tsx +53 -24
- package/corpus/templates/clips/desktop/src/lib/bubble-playback.ts +68 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +3 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +72 -14
- package/corpus/templates/clips/desktop/src/lib/updater.ts +8 -3
- package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +162 -20
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +43 -6
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +150 -13
- package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +12 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +28 -3
- package/corpus/templates/clips/server/lib/emails.ts +17 -33
- package/corpus/templates/content/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +140 -15
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +12 -14
- package/corpus/templates/content/app/components/ui/pointer-lock.ts +1 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +18 -2
- package/corpus/templates/content/app/hooks/use-local-storage.ts +38 -22
- package/corpus/templates/content/app/i18n-data.ts +25 -0
- package/corpus/templates/content/app/routes.ts +3 -1
- package/corpus/templates/content/changelog/2026-08-22-code-blocks-inserted-from-the-slash-menu-no-longer-get-stuck.md +6 -0
- package/corpus/templates/content/changelog/2026-08-22-the-sidebar-page-tree-no-longer-flashes-empty-after-creating-a-page.md +6 -0
- package/corpus/templates/content/docs/solutions/2026-08-21-content-mermaid-rendering-shape.md +233 -0
- package/corpus/templates/content/server/plugins/agent-chat.ts +9 -2
- package/corpus/templates/content/vitest.config.ts +7 -1
- package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/design/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +1383 -88
- package/corpus/templates/design/actions/generate-design.ts +98 -47
- package/corpus/templates/design/actions/get-design-system.ts +6 -1
- package/corpus/templates/design/actions/refresh-design-system-with-builder.ts +146 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +70 -26
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +284 -136
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2057 -141
- package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +6 -2
- package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +4 -4
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +10 -1
- package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +19 -2
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -2
- package/corpus/templates/design/app/components/layout/Header.tsx +15 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +7 -2
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -4
- package/corpus/templates/design/app/i18n-data.ts +1 -0
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +12 -0
- package/corpus/templates/design/app/lib/design-system-data.ts +68 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +77 -35
- package/corpus/templates/design/app/pages/DesignSystems.tsx +136 -42
- package/corpus/templates/design/app/pages/Index.tsx +2 -0
- package/corpus/templates/design/app/pages/Templates.tsx +0 -10
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +10 -0
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +50 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +10 -8
- package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +6 -6
- package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +18 -7
- package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +4 -90
- package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +2 -1
- package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +4 -3
- package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +12 -2
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +9 -2
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +11 -1
- package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +11 -19
- package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +6 -6
- package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +12 -15
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -59
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +8 -0
- package/corpus/templates/design/app/pages/design-editor/types.ts +3 -3
- package/corpus/templates/design/app/routes.ts +3 -1
- package/corpus/templates/design/changelog/2026-08-20-canvas-edits-patch-the-frame-instead-of-rebuilding-it.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-builder-design-system-imports-reconcile-after-indexing.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-draw-mode-needs-shift-y.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-eyedropper-shortcut.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-figma-style-alignment-guides-and-snapping.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-group-transforms-keep-the-selection.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-inspector-fields-keep-focus-on-enter.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-pen-alt-keeps-the-incoming-handle.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-pen-on-the-board-creates-one-vector.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-scale-a-multi-selection-from-the-group-handles.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-scale-resizes-text-with-the-object.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-selecting-another-object-keeps-the-scale-tool-armed.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-the-shape-tool-remembers-the-shape-you-picked.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-vectors-drawn-in-a-frame-land-where-you-drew-them.md +6 -0
- package/corpus/templates/design/changelog/2026-08-22-multi-screen-generation-no-longer-loses-a-saved-screens-layo.md +6 -0
- package/corpus/templates/design/changelog/2026-08-22-rename-and-search-fields-are-labeled-for-screen-readers.md +6 -0
- package/corpus/templates/design/package.json +2 -0
- package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +311 -0
- package/corpus/templates/design/shared/canvas-math.ts +612 -206
- package/corpus/templates/design/shared/pen-path.ts +37 -0
- package/corpus/templates/design/vitest.config.ts +7 -1
- package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/dispatch/actions/list-workspace-connections.ts +34 -3
- package/corpus/templates/dispatch/app/routes/integrations.tsx +55 -21
- package/corpus/templates/dispatch/app/routes.ts +3 -1
- package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/factory/.env.example +2 -7
- package/corpus/templates/factory/AGENTS.md +3 -0
- package/corpus/templates/factory/README.md +5 -3
- package/corpus/templates/factory/actions/create-factory.ts +16 -3
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +2 -0
- package/corpus/templates/factory/actions/get-triage-item.ts +2 -0
- package/corpus/templates/factory/actions/list-factory-audit.ts +116 -36
- package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
- package/corpus/templates/factory/actions/list-triage-items.ts +22 -19
- package/corpus/templates/factory/actions/run-factory-automation.ts +7 -3
- package/corpus/templates/factory/actions/save-factory-automation.ts +5 -2
- package/corpus/templates/factory/actions/save-triage-config.ts +2 -8
- package/corpus/templates/factory/actions/start-builder-for-item.ts +21 -0
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +229 -297
- package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +397 -247
- package/corpus/templates/factory/app/components/factory/FactorySourceSettingsGroup.tsx +33 -0
- package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +202 -162
- package/corpus/templates/factory/app/i18n/en-US.ts +32 -4
- package/corpus/templates/factory/app/routes/factory.tsx +84 -11
- package/corpus/templates/factory/app/routes.ts +3 -1
- package/corpus/templates/factory/changelog/2026-08-21-activity-runs-use-automation-display-names.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-21-create-and-settings-source-cards.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-21-keep-automations-visible-while-running.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-21-nested-factory-automations-can-poll.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-21-settings-sticky-unsaved-bar.md +6 -0
- package/corpus/templates/factory/server/connectors/credentials.ts +10 -5
- package/corpus/templates/factory/server/lib/factory-audit-report.ts +412 -0
- package/corpus/templates/factory/server/lib/factory-config-reconcile.ts +321 -0
- package/corpus/templates/factory/server/lib/factory-scope.ts +25 -2
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +13 -4
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +148 -4
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +4 -6
- package/corpus/templates/factory/vitest.config.ts +17 -2
- package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/forms/app/routes/_app.settings.tsx +4 -1
- package/corpus/templates/forms/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +11 -1
- package/corpus/templates/forms/vitest.config.ts +1 -0
- package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/macros/app/routes.ts +3 -1
- package/corpus/templates/macros/vitest.config.ts +6 -1
- package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/mail/actions/manage-gmail-filters.ts +10 -2
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -37
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +14 -2
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +66 -36
- package/corpus/templates/mail/app/hooks/use-google-auth.ts +2 -0
- package/corpus/templates/mail/app/lib/google-oauth-setup.ts +17 -0
- package/corpus/templates/mail/app/pages/InboxPage.tsx +69 -9
- package/corpus/templates/mail/app/routes/$.tsx +9 -0
- package/corpus/templates/mail/app/routes/$view.$threadId.tsx +23 -1
- package/corpus/templates/mail/app/routes/$view.tsx +47 -1
- package/corpus/templates/mail/app/routes.ts +3 -1
- package/corpus/templates/mail/changelog/2026-08-22-gmail-can-now-connect-with-the-shared-google-oauth.md +6 -0
- package/corpus/templates/mail/changelog/2026-08-22-gmail-filters-now-shows-the-real-error.md +6 -0
- package/corpus/templates/mail/changelog/2026-08-22-unknown-mail-urls-now-show-the-404-page.md +6 -0
- package/corpus/templates/mail/server/handlers/google-auth.ts +11 -2
- package/corpus/templates/mail/server/lib/google-auth.ts +92 -12
- package/corpus/templates/mail/server/onboarding.ts +66 -18
- package/corpus/templates/mail/vitest.config.ts +6 -1
- package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/plan/app/routes/settings.tsx +4 -1
- package/corpus/templates/plan/app/routes.ts +3 -1
- package/corpus/templates/plan/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
- package/corpus/templates/plan/netlify.toml +1 -1
- package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/slides/actions/patch-deck.ts +79 -10
- package/corpus/templates/slides/actions/update-slide.ts +11 -1
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +14 -4
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +10 -52
- package/corpus/templates/slides/app/components/editor/GoogleDocImportHint.tsx +8 -53
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +9 -75
- package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +8 -60
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +10 -2
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +19 -7
- package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +49 -36
- package/corpus/templates/slides/app/routes/access-request.approve.tsx +16 -9
- package/corpus/templates/slides/app/routes.ts +3 -1
- package/corpus/templates/slides/changelog/2026-08-22-already-uploaded-chat-images-are-no-longer-dropped.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-22-google-drive-and-slides-can-now-connect-with-the-shared-google-oauth.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-22-multi-slide-repairs-now-report-slides-that-still-overflow.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-22-update-slide-now-reports-which-batched-edit-was-skipped.md +6 -0
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +52 -1
- package/corpus/templates/slides/server/lib/chat-attachments.ts +29 -3
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +66 -16
- package/corpus/templates/slides/server/lib/slide-content-patch.ts +13 -2
- package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -2
- package/corpus/templates/slides/tsconfig.json +5 -0
- package/corpus/templates/slides/vitest.config.ts +6 -1
- package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/tasks/app/routes/settings.tsx +4 -1
- package/corpus/templates/tasks/app/routes.ts +3 -1
- package/corpus/templates/tasks/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
- package/corpus/templates/tasks/vitest.config.ts +7 -1
- package/dist/action.d.ts +91 -0
- package/dist/action.js +50 -0
- package/dist/agent/abort-reasons.d.ts +38 -0
- package/dist/agent/abort-reasons.js +49 -0
- package/dist/agent/engine/ai-sdk-engine.js +8 -5
- package/dist/agent/engine/anthropic-engine.js +7 -5
- package/dist/agent/engine/first-event-timeout.d.ts +42 -17
- package/dist/agent/engine/first-event-timeout.js +67 -25
- package/dist/agent/production-agent.d.ts +23 -0
- package/dist/agent/production-agent.js +107 -102
- package/dist/agent/run-loop-with-resume.d.ts +2 -28
- package/dist/agent/run-loop-with-resume.js +25 -40
- package/dist/agent/run-manager.js +5 -4
- package/dist/agent/run-store.d.ts +55 -0
- package/dist/agent/run-store.js +124 -1
- package/dist/agent/tool-search.js +2 -1
- package/dist/agent/types.d.ts +9 -4
- package/dist/app-config/configuration-error.d.ts +11 -0
- package/dist/app-config/configuration-error.js +14 -0
- package/dist/app-config/index.d.ts +1 -0
- package/dist/app-config/index.js +1 -0
- package/dist/app-config/integrations.d.ts +1 -0
- package/dist/app-config/integrations.js +10 -0
- package/dist/app-config/plugins.d.ts +34 -0
- package/dist/app-config/plugins.js +39 -0
- package/dist/app-config/run-lifecycle-invariants.d.ts +18 -9
- package/dist/app-config/run-lifecycle-invariants.js +18 -25
- package/dist/app-config/schema.d.ts +16 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/chat-threads/schema-migrations.js +20 -0
- package/dist/chat-threads/store.js +21 -4
- package/dist/cli/native-dependencies.d.ts +4 -0
- package/dist/cli/native-dependencies.js +15 -0
- package/dist/client/EnvironmentBadge.d.ts +1 -1
- package/dist/client/EnvironmentBadge.js +40 -5
- package/dist/client/MultiTabAssistantChat.d.ts +1 -1
- package/dist/client/MultiTabAssistantChat.js +35 -14
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat/index.js +1 -1
- package/dist/client/agent-page/use-jobs.d.ts +2 -1
- package/dist/client/agent-page/use-jobs.js +3 -5
- package/dist/client/agent-sidebar-state.js +3 -1
- package/dist/client/blocks/library/code-tabs.js +5 -0
- package/dist/client/blocks/library/code.js +1 -0
- package/dist/client/chat-first/apps-rail.js +2 -2
- package/dist/client/chat-model-groups.d.ts +12 -0
- package/dist/client/chat-model-groups.js +3 -2
- package/dist/client/integrations/GoogleProductLogo.d.ts +5 -0
- package/dist/client/integrations/GoogleProductLogo.js +45 -0
- package/dist/client/integrations/index.d.ts +2 -0
- package/dist/client/integrations/index.js +2 -0
- package/dist/client/integrations/workspace-provider-oauth.d.ts +8 -0
- package/dist/client/integrations/workspace-provider-oauth.js +13 -0
- package/dist/client/resources/mcp-integration-catalog.js +0 -29
- package/dist/client/settings/SettingsTabsPage.js +10 -6
- package/dist/client/sharing/ShareButton.js +11 -2
- package/dist/client/use-chat-models.d.ts +6 -0
- package/dist/client/use-chat-models.js +10 -0
- package/dist/client/use-db-sync.js +27 -9
- package/dist/connections/catalog.d.ts +111 -9
- package/dist/connections/catalog.js +86 -30
- package/dist/db/create-get-db.js +18 -0
- package/dist/db/ddl-guard.d.ts +0 -4
- package/dist/db/ddl-guard.js +46 -0
- package/dist/deploy/build.d.ts +19 -0
- package/dist/deploy/build.js +190 -16
- package/dist/deploy/function-bundle.d.ts +45 -0
- package/dist/deploy/function-bundle.js +337 -0
- package/dist/deploy/workspace-deploy.js +19 -1
- package/dist/eject/workspace-connections.js +4 -0
- package/dist/email-catalog/registry.d.ts +4 -0
- package/dist/email-catalog/registry.js +79 -14
- package/dist/extensions/content-patch.js +14 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/plugin.d.ts +10 -0
- package/dist/integrations/plugin.js +40 -6
- package/dist/jobs/actions/run-automation-now.d.ts +2 -1
- package/dist/jobs/actions/run-automation-now.js +11 -5
- package/dist/jobs/frontmatter.js +53 -2
- package/dist/jobs/run-now.d.ts +11 -1
- package/dist/jobs/run-now.js +42 -7
- package/dist/jobs/scheduler.d.ts +1 -0
- package/dist/jobs/scheduler.js +2 -1
- package/dist/localization/core-messages/ar-SA.js +3 -0
- package/dist/localization/core-messages/de-DE.js +3 -0
- package/dist/localization/core-messages/en-US.d.ts +3 -0
- package/dist/localization/core-messages/en-US.js +3 -0
- package/dist/localization/core-messages/es-ES.js +3 -0
- package/dist/localization/core-messages/fr-FR.js +3 -0
- package/dist/localization/core-messages/hi-IN.js +3 -0
- package/dist/localization/core-messages/ja-JP.js +3 -0
- package/dist/localization/core-messages/ko-KR.js +3 -0
- package/dist/localization/core-messages/pt-BR.js +3 -0
- package/dist/localization/core-messages/zh-CN.js +3 -0
- package/dist/localization/core-messages/zh-TW.js +3 -0
- package/dist/localization/core-messages.d.ts +3 -0
- package/dist/localization/default-messages.d.ts +2 -5
- package/dist/localization/default-messages.js +2 -5
- package/dist/mcp/build-server.d.ts +9 -0
- package/dist/mcp/build-server.js +37 -8
- package/dist/mcp-client/oauth-client.d.ts +7 -0
- package/dist/mcp-client/oauth-client.js +12 -1
- package/dist/oauth-tokens/index.d.ts +1 -1
- package/dist/oauth-tokens/index.js +1 -1
- package/dist/oauth-tokens/lifecycle.d.ts +11 -0
- package/dist/oauth-tokens/lifecycle.js +17 -0
- package/dist/oauth-tokens/store.js +7 -0
- package/dist/observability/posthog-ai.d.ts +15 -1
- package/dist/observability/posthog-ai.js +24 -12
- package/dist/observability/routes.d.ts +3 -3
- package/dist/observability/traces.js +158 -28
- package/dist/onboarding/app-profile.js +2 -2
- package/dist/onboarding/plugin.js +10 -2
- package/dist/onboarding/types.d.ts +2 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/provider-api/index.js +2 -0
- package/dist/resources/store.js +13 -4
- package/dist/scripts/agent-engines/list-agent-engines.js +29 -3
- package/dist/secrets/register-framework-secrets.js +1 -1
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/agent-chat/action-filters-a2a.d.ts +27 -3
- package/dist/server/agent-chat/action-filters-a2a.js +48 -9
- package/dist/server/agent-chat-plugin.js +36 -9
- package/dist/server/agent-discovery.js +43 -20
- package/dist/server/auth.js +18 -16
- package/dist/server/beta-opt-out-html.js +45 -4
- package/dist/server/better-auth-instance.d.ts +2 -1
- package/dist/server/better-auth-instance.js +23 -7
- package/dist/server/builder-design-systems.d.ts +4 -1
- package/dist/server/builder-design-systems.js +87 -6
- package/dist/server/builder-oauth.d.ts +1 -7
- package/dist/server/builder-oauth.js +80 -163
- package/dist/server/core-routes-plugin.d.ts +29 -0
- package/dist/server/core-routes-plugin.js +100 -20
- package/dist/server/default-plugins.d.ts +18 -0
- package/dist/server/default-plugins.js +22 -0
- package/dist/server/edge.d.ts +1 -0
- package/dist/server/edge.js +1 -0
- package/dist/server/framework-request-handler.js +31 -3
- package/dist/server/google-oauth.d.ts +3 -0
- package/dist/server/google-oauth.js +3 -0
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +3 -3
- package/dist/server/onboarding-html.js +37 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/workspace-provider-oauth.d.ts +6 -1
- package/dist/server/workspace-provider-oauth.js +72 -20
- package/dist/shared/agent-sidebar-url.d.ts +1 -0
- package/dist/shared/agent-sidebar-url.js +1 -0
- package/dist/shared/environment-lanes.d.ts +2 -0
- package/dist/shared/environment-lanes.js +3 -1
- package/dist/shared/index.d.ts +3 -1
- package/dist/shared/index.js +3 -1
- package/dist/shared/surface-visibility.d.ts +31 -0
- package/dist/shared/surface-visibility.js +57 -0
- package/dist/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
- package/dist/templates/chat/app/routes.ts +3 -1
- package/dist/templates/chat/vitest.config.ts +8 -0
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +11 -4
- package/dist/templates/default/app/routes.ts +3 -1
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
- package/dist/tracking/registry.d.ts +11 -0
- package/dist/tracking/registry.js +5 -2
- package/dist/triggers/actions.js +10 -3
- package/dist/usage/store.js +23 -3
- package/dist/user-profile/actions/get-auth-methods.js +18 -5
- package/dist/vite/client.js +24 -5
- package/docs/content/actions-access-control.mdx +43 -4
- package/docs/content/actions-defining.mdx +16 -0
- package/docs/content/template-clips-capture-everywhere.mdx +29 -30
- package/docs/content/template-clips-developers.mdx +4 -4
- package/docs/content/template-clips.mdx +3 -1
- package/package.json +2 -2
- package/src/eject/workspace-connections.ts +4 -0
- package/src/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
- package/src/templates/chat/app/routes.ts +3 -1
- package/src/templates/chat/vitest.config.ts +8 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +11 -4
- package/src/templates/default/app/routes.ts +3 -1
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
|
@@ -506,6 +506,7 @@ export class RecorderEngine {
|
|
|
506
506
|
private audioMixSources: MediaStreamAudioSourceNode[] = [];
|
|
507
507
|
private wakeLock: WakeLockSentinel | null = null;
|
|
508
508
|
private wakeLockGeneration = 0;
|
|
509
|
+
private wakeLockRetake: (() => void) | null = null;
|
|
509
510
|
private recorder: MediaRecorder | null = null;
|
|
510
511
|
private mimeType: string = "video/webm";
|
|
511
512
|
|
|
@@ -909,7 +910,7 @@ export class RecorderEngine {
|
|
|
909
910
|
// during active recording, which ends getDisplayMedia's video track
|
|
910
911
|
// outright (unlike the mic/camera, display capture requires an
|
|
911
912
|
// active compositor). Without this, a paused recording left idle
|
|
912
|
-
// finalizes early
|
|
913
|
+
// finalizes early — see onDisplayTrackEnded below.
|
|
913
914
|
// Best-effort: unsupported browsers or a denied request must never
|
|
914
915
|
// block capture.
|
|
915
916
|
void this.acquireWakeLock();
|
|
@@ -1010,6 +1011,14 @@ export class RecorderEngine {
|
|
|
1010
1011
|
);
|
|
1011
1012
|
track.addEventListener("ended", () => {
|
|
1012
1013
|
if (this.state === "recording" || this.state === "paused") {
|
|
1014
|
+
// Paused, the user cannot have clicked "Stop sharing" — the
|
|
1015
|
+
// display slept or locked. Same finalize, but say so instead of
|
|
1016
|
+
// ending as if they had asked for it.
|
|
1017
|
+
if (this.state === "paused") {
|
|
1018
|
+
this.emitWarning(
|
|
1019
|
+
"Screen sharing ended while the recording was paused; saving what was captured so far.",
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1013
1022
|
if (this.opts.onDisplayTrackEnded) {
|
|
1014
1023
|
this.opts.onDisplayTrackEnded();
|
|
1015
1024
|
} else {
|
|
@@ -2371,7 +2380,22 @@ export class RecorderEngine {
|
|
|
2371
2380
|
|
|
2372
2381
|
/** Best-effort — unsupported browsers or a denied request must never block capture. */
|
|
2373
2382
|
private async acquireWakeLock(): Promise<void> {
|
|
2383
|
+
// The platform drops a screen wake lock the moment the document goes
|
|
2384
|
+
// hidden and refuses to grant one while it is hidden, so a single
|
|
2385
|
+
// acquisition only ever protects the foreground. Taking it again on the
|
|
2386
|
+
// way back is the most the page can do.
|
|
2387
|
+
if (!this.wakeLockRetake && typeof document !== "undefined") {
|
|
2388
|
+
const doc = document;
|
|
2389
|
+
this.wakeLockRetake = () => {
|
|
2390
|
+
if (doc.visibilityState === "visible" && this.displayStream) {
|
|
2391
|
+
void this.acquireWakeLock();
|
|
2392
|
+
}
|
|
2393
|
+
};
|
|
2394
|
+
doc.addEventListener("visibilitychange", this.wakeLockRetake);
|
|
2395
|
+
}
|
|
2374
2396
|
const generation = ++this.wakeLockGeneration;
|
|
2397
|
+
this.wakeLock?.release().catch(() => {});
|
|
2398
|
+
this.wakeLock = null;
|
|
2375
2399
|
try {
|
|
2376
2400
|
// coercion-ok: optional chaining yields undefined only when the Wake
|
|
2377
2401
|
// Lock API itself is absent (older/other browsers) — a real rejection
|
|
@@ -2389,6 +2413,10 @@ export class RecorderEngine {
|
|
|
2389
2413
|
|
|
2390
2414
|
private releaseWakeLock(): void {
|
|
2391
2415
|
this.wakeLockGeneration += 1;
|
|
2416
|
+
if (this.wakeLockRetake) {
|
|
2417
|
+
document.removeEventListener("visibilitychange", this.wakeLockRetake);
|
|
2418
|
+
this.wakeLockRetake = null;
|
|
2419
|
+
}
|
|
2392
2420
|
this.wakeLock?.release().catch(() => {});
|
|
2393
2421
|
this.wakeLock = null;
|
|
2394
2422
|
}
|
|
@@ -351,6 +351,7 @@ const messages = {
|
|
|
351
351
|
},
|
|
352
352
|
meetingDetail: {
|
|
353
353
|
untitledMeeting: "Untitled meeting",
|
|
354
|
+
recordedBy: "Recorded by {{name}}",
|
|
354
355
|
unassigned: "Unassigned",
|
|
355
356
|
them: "Them",
|
|
356
357
|
me: "Me",
|
|
@@ -358,6 +359,10 @@ const messages = {
|
|
|
358
359
|
meetingRemoved: "Meeting removed",
|
|
359
360
|
couldNotRemoveMeeting: "Couldn't remove meeting",
|
|
360
361
|
couldNotLoadMeeting: "Couldn't load this meeting.",
|
|
362
|
+
retry: "Retry",
|
|
363
|
+
meetingNotFound: "Meeting not found",
|
|
364
|
+
meetingUnavailable: "This meeting isn't available.",
|
|
365
|
+
noAccess: "You may not have access to this meeting.",
|
|
361
366
|
transcriptCopied: "Transcript copied",
|
|
362
367
|
couldNotCopyTranscript: "Couldn't copy transcript",
|
|
363
368
|
allMeetings: "All meetings",
|
|
@@ -466,6 +466,7 @@ export default function MeetingDetailRoute() {
|
|
|
466
466
|
} | null;
|
|
467
467
|
recording?: { id: string; durationMs?: number | null } | null;
|
|
468
468
|
role?: "owner" | "admin" | "editor" | "commenter" | "viewer";
|
|
469
|
+
reason?: "unavailable";
|
|
469
470
|
};
|
|
470
471
|
|
|
471
472
|
const {
|
|
@@ -1018,7 +1019,28 @@ export default function MeetingDetailRoute() {
|
|
|
1018
1019
|
finalize,
|
|
1019
1020
|
]);
|
|
1020
1021
|
|
|
1021
|
-
|
|
1022
|
+
// Failed, still pending, and loaded-but-absent are three outcomes: the query
|
|
1023
|
+
// never retries, so collapsing any of them into the skeleton pins it forever.
|
|
1024
|
+
// A failed live-poll on top of an already-loaded meeting is none of them —
|
|
1025
|
+
// keep showing the meeting.
|
|
1026
|
+
if (isError && !meeting) {
|
|
1027
|
+
return (
|
|
1028
|
+
<div className="p-6 max-w-2xl mx-auto w-full">
|
|
1029
|
+
<div className="rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive">
|
|
1030
|
+
{t("meetingDetail.couldNotLoadMeeting")}
|
|
1031
|
+
</div>
|
|
1032
|
+
<Button
|
|
1033
|
+
variant="outline"
|
|
1034
|
+
className="mt-3"
|
|
1035
|
+
onClick={() => refetchMeeting()}
|
|
1036
|
+
>
|
|
1037
|
+
{t("meetingDetail.retry")}
|
|
1038
|
+
</Button>
|
|
1039
|
+
</div>
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
if (isLoading) {
|
|
1022
1044
|
return (
|
|
1023
1045
|
<div className="p-6 max-w-6xl mx-auto w-full">
|
|
1024
1046
|
<Skeleton className="h-6 w-32 mb-4" />
|
|
@@ -1032,12 +1054,19 @@ export default function MeetingDetailRoute() {
|
|
|
1032
1054
|
);
|
|
1033
1055
|
}
|
|
1034
1056
|
|
|
1035
|
-
if (
|
|
1057
|
+
if (!meeting) {
|
|
1036
1058
|
return (
|
|
1037
1059
|
<div className="p-6 max-w-2xl mx-auto w-full">
|
|
1038
|
-
<div className="rounded-md border
|
|
1039
|
-
{
|
|
1060
|
+
<div className="rounded-md border px-4 py-3 text-sm text-muted-foreground">
|
|
1061
|
+
{/* Neutral on purpose: `get-meeting` returns one reason for missing
|
|
1062
|
+
and inaccessible so callers cannot probe which ids exist, and
|
|
1063
|
+
saying "not found" here would leak back the distinction the
|
|
1064
|
+
action withholds. */}
|
|
1065
|
+
{t("meetingDetail.meetingUnavailable")}
|
|
1040
1066
|
</div>
|
|
1067
|
+
<Button asChild variant="outline" className="mt-3">
|
|
1068
|
+
<NavLink to="/meetings">{t("meetingDetail.allMeetings")}</NavLink>
|
|
1069
|
+
</Button>
|
|
1041
1070
|
</div>
|
|
1042
1071
|
);
|
|
1043
1072
|
}
|
|
@@ -43,16 +43,23 @@ export default function ApproveRecordingAccessRequestRoute() {
|
|
|
43
43
|
const approvalTokenFromUrl = searchParams.get("token") ?? "";
|
|
44
44
|
const approvalTokenStorageKey =
|
|
45
45
|
recordingAccessApprovalSessionKey(recordingId);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
// Only the URL token is knowable on the server, so reading storage in the
|
|
47
|
+
// initializer makes the first client render disagree with the server's and
|
|
48
|
+
// React re-renders the page from scratch. Adopt the stored token after mount.
|
|
49
|
+
const [approvalToken, setApprovalToken] = useState(
|
|
50
|
+
() => approvalTokenFromUrl ?? "",
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (approvalTokenFromUrl || !recordingId) return;
|
|
49
55
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
const stored = sessionStorage.getItem(approvalTokenStorageKey);
|
|
57
|
+
if (stored) setApprovalToken(stored);
|
|
58
|
+
// Unavailable tab storage is an absent continuation: the page then asks
|
|
59
|
+
// for the token rather than silently continuing without one.
|
|
60
|
+
// coercion-ok: the absent case is visible to the user, not swallowed.
|
|
61
|
+
} catch {}
|
|
62
|
+
}, [approvalTokenFromUrl, recordingId, approvalTokenStorageKey]);
|
|
56
63
|
const [state, setState] = useState<ApprovalState>({ kind: "loading" });
|
|
57
64
|
|
|
58
65
|
useEffect(() => {
|
|
@@ -71,14 +71,24 @@ export default function EmbedRoute() {
|
|
|
71
71
|
[searchParams],
|
|
72
72
|
);
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// Same hydration trap the share route had: reading sessionStorage in the
|
|
75
|
+
// initializer makes the first client render disagree with the server's,
|
|
76
|
+
// which has no storage and always renders the locked state. React discards
|
|
77
|
+
// the hydrated tree and re-renders from scratch, so an embedded player goes
|
|
78
|
+
// blank for a returning viewer. Start where the server started and adopt the
|
|
79
|
+
// stored password after mount.
|
|
80
|
+
const [password, setPassword] = useState<string | null>(null);
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (!shareId) return;
|
|
76
84
|
try {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
const stored = sessionStorage.getItem(STORAGE_KEY_PREFIX + shareId);
|
|
86
|
+
if (stored) setPassword(stored);
|
|
87
|
+
// Unreadable storage and no stored password are the same state here:
|
|
88
|
+
// both leave `password` null, which renders the password prompt.
|
|
89
|
+
// coercion-ok: the fallback is visible to the viewer, not swallowed.
|
|
90
|
+
} catch {}
|
|
91
|
+
}, [shareId]);
|
|
82
92
|
const [pwError, setPwError] = useState<string | null>(null);
|
|
83
93
|
const readyMediaPollRef = useRef<{ key: string; until: number } | null>(null);
|
|
84
94
|
|
|
@@ -443,14 +443,24 @@ export default function ShareRoute() {
|
|
|
443
443
|
|
|
444
444
|
const playerRef = useRef<VideoPlayerHandle | null>(null);
|
|
445
445
|
const readyMediaPollRef = useRef<{ key: string; until: number } | null>(null);
|
|
446
|
-
|
|
447
|
-
|
|
446
|
+
// Reading sessionStorage in the initializer makes the first client render
|
|
447
|
+
// disagree with the server's, which has no storage and always renders the
|
|
448
|
+
// locked state. React answers a mismatch by throwing away the hydrated tree
|
|
449
|
+
// and re-rendering from scratch, so a returning viewer watches a blank share
|
|
450
|
+
// page while everything refetches. Start where the server started and adopt
|
|
451
|
+
// the stored password after mount.
|
|
452
|
+
const [password, setPassword] = useState<string | null>(null);
|
|
453
|
+
|
|
454
|
+
useEffect(() => {
|
|
455
|
+
if (!shareId) return;
|
|
448
456
|
try {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
457
|
+
const stored = sessionStorage.getItem(STORAGE_KEY_PREFIX + shareId);
|
|
458
|
+
if (stored) setPassword(stored);
|
|
459
|
+
// Unreadable storage and no stored password are the same state to this
|
|
460
|
+
// screen: both leave `password` null, which renders the password prompt.
|
|
461
|
+
// coercion-ok: the fallback is visible to the viewer, not swallowed.
|
|
462
|
+
} catch {}
|
|
463
|
+
}, [shareId]);
|
|
454
464
|
const [pwError, setPwError] = useState<string | null>(null);
|
|
455
465
|
const [currentMs, setCurrentMs] = useState(0);
|
|
456
466
|
const [commentOpen, setCommentOpen] = useState(false);
|
|
@@ -1330,8 +1330,7 @@ async function armRecording(args: {
|
|
|
1330
1330
|
sourceAppName: "Chrome",
|
|
1331
1331
|
sourceWindowTitle: tab.title ?? null,
|
|
1332
1332
|
hasCamera: cameraInvolved,
|
|
1333
|
-
hasAudio:
|
|
1334
|
-
settings.includeMicrophone || settings.captureSurface !== "camera",
|
|
1333
|
+
hasAudio: settings.includeMicrophone,
|
|
1335
1334
|
mimeType: "video/webm",
|
|
1336
1335
|
requestStreaming: true,
|
|
1337
1336
|
});
|
|
@@ -43,6 +43,7 @@ export interface NativeTranscriptionCapture {
|
|
|
43
43
|
|
|
44
44
|
const STOP_SETTLE_MS = 1_500;
|
|
45
45
|
const RESTART_DELAY_MS = 250;
|
|
46
|
+
const MAX_RESTART_ATTEMPTS = 8;
|
|
46
47
|
|
|
47
48
|
function getSpeechRecognitionCtor(): SpeechRecognitionConstructor | null {
|
|
48
49
|
if (typeof window === "undefined") return null;
|
|
@@ -81,20 +82,29 @@ export function createNativeTranscriptionCapture(options?: {
|
|
|
81
82
|
let paused = false;
|
|
82
83
|
let disposed = false;
|
|
83
84
|
let failureReason = unsupportedReason;
|
|
85
|
+
// Kept apart from `failureReason`: a failed `start()` attempt loses no
|
|
86
|
+
// audio once a later attempt succeeds, whereas `failureReason` records
|
|
87
|
+
// speech that was actually dropped and must survive a recovery.
|
|
88
|
+
let restartStartFailure: string | null = null;
|
|
84
89
|
let restartTimer: ReturnType<typeof setTimeout> | null = null;
|
|
90
|
+
let restartFailures = 0;
|
|
85
91
|
let stopPromise: Promise<NativeTranscriptResult> | null = null;
|
|
86
92
|
let resolveStop: ((result: NativeTranscriptResult) => void) | null = null;
|
|
87
93
|
let stopTimer: ReturnType<typeof setTimeout> | null = null;
|
|
88
94
|
|
|
89
95
|
const result = (): NativeTranscriptResult => {
|
|
90
96
|
const text = appendTranscript(finalText, interimText).trim();
|
|
97
|
+
// Captured text does not clear a failure: a capture that died mid-recording
|
|
98
|
+
// still has to be re-transcribed in the cloud, and the reason is the only
|
|
99
|
+
// signal the server has that this transcript is truncated.
|
|
91
100
|
return {
|
|
92
101
|
text,
|
|
93
102
|
failureReason:
|
|
94
|
-
|
|
103
|
+
failureReason ||
|
|
104
|
+
restartStartFailure ||
|
|
105
|
+
(text.length > 0
|
|
95
106
|
? null
|
|
96
|
-
:
|
|
97
|
-
"Chrome Web Speech recognition returned no transcript.",
|
|
107
|
+
: "Chrome Web Speech recognition returned no transcript."),
|
|
98
108
|
};
|
|
99
109
|
};
|
|
100
110
|
|
|
@@ -111,20 +121,34 @@ export function createNativeTranscriptionCapture(options?: {
|
|
|
111
121
|
resolve(result());
|
|
112
122
|
};
|
|
113
123
|
|
|
114
|
-
const scheduleRestart = (): void => {
|
|
124
|
+
const scheduleRestart = (delayMs: number = RESTART_DELAY_MS): void => {
|
|
115
125
|
if (restartTimer !== null) return;
|
|
116
126
|
restartTimer = setTimeout(() => {
|
|
117
127
|
restartTimer = null;
|
|
118
128
|
if (disposed || stopped || paused || !recognition) return;
|
|
119
129
|
try {
|
|
120
130
|
recognition.start();
|
|
131
|
+
restartFailures = 0;
|
|
132
|
+
// The engine recovered, so a failed start attempt is no longer the
|
|
133
|
+
// transcript's outcome. `failureReason` is deliberately NOT cleared:
|
|
134
|
+
// it records audio that was actually dropped, which a restart cannot
|
|
135
|
+
// recover.
|
|
136
|
+
restartStartFailure = null;
|
|
121
137
|
} catch (error) {
|
|
122
|
-
|
|
138
|
+
restartStartFailure =
|
|
123
139
|
error instanceof Error
|
|
124
140
|
? `Chrome Web Speech recognition could not restart: ${error.message}`
|
|
125
141
|
: "Chrome Web Speech recognition could not restart.";
|
|
142
|
+
// Nothing started, so no `onend` will arrive to schedule the next try.
|
|
143
|
+
restartFailures += 1;
|
|
144
|
+
if (restartFailures < MAX_RESTART_ATTEMPTS) {
|
|
145
|
+
scheduleRestart(RESTART_DELAY_MS * restartFailures);
|
|
146
|
+
} else {
|
|
147
|
+
// Out of retries: the gap is permanent, so it stops being transient.
|
|
148
|
+
failureReason = failureReason || restartStartFailure;
|
|
149
|
+
}
|
|
126
150
|
}
|
|
127
|
-
},
|
|
151
|
+
}, delayMs);
|
|
128
152
|
};
|
|
129
153
|
|
|
130
154
|
const start = (): void => {
|
|
@@ -230,10 +254,18 @@ export function createNativeTranscriptionCapture(options?: {
|
|
|
230
254
|
const resume = (): void => {
|
|
231
255
|
if (!recognition || stopped || disposed) return;
|
|
232
256
|
paused = false;
|
|
257
|
+
restartFailures = 0;
|
|
233
258
|
try {
|
|
234
259
|
recognition.start();
|
|
235
260
|
} catch {
|
|
236
|
-
|
|
261
|
+
// Chrome throws a transient InvalidStateError if the previous session has
|
|
262
|
+
// not finished tearing down. Nothing started, so no `onend` will arrive to
|
|
263
|
+
// drive the bounded retry loop — schedule it here or transcription stays
|
|
264
|
+
// dead for the rest of the recording. The gap is recorded durably because
|
|
265
|
+
// audio is already live: `paused` is false above.
|
|
266
|
+
failureReason =
|
|
267
|
+
failureReason || "Chrome Web Speech recognition could not resume.";
|
|
268
|
+
scheduleRestart();
|
|
237
269
|
}
|
|
238
270
|
};
|
|
239
271
|
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import { scheduleReadyChime } from "@shared/recording-audio";
|
|
26
26
|
import {
|
|
27
27
|
SCREEN_CAPTURE_FRAME_RATE,
|
|
28
|
-
|
|
28
|
+
screenCaptureDisplayOptions,
|
|
29
29
|
type ScreenCaptureSurface,
|
|
30
30
|
} from "@shared/recording-capture";
|
|
31
31
|
import {
|
|
@@ -327,17 +327,17 @@ async function streamDimensions(
|
|
|
327
327
|
};
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
|
|
330
|
+
// Gates screen/tab audio on the same "Include microphone" toggle the popup
|
|
331
|
+
// shows for the mic stream — see screenCaptureDisplayOptions for why: mic off
|
|
332
|
+
// is the only signal the user gets to say "capture no audio at all".
|
|
333
|
+
export function displayConstraints(
|
|
331
334
|
surface: ScreenCaptureSurface,
|
|
335
|
+
wantsMic: boolean,
|
|
332
336
|
): MediaStreamConstraints {
|
|
333
|
-
return
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
noiseSuppression: false,
|
|
338
|
-
autoGainControl: false,
|
|
339
|
-
},
|
|
340
|
-
} as MediaStreamConstraints;
|
|
337
|
+
return screenCaptureDisplayOptions(
|
|
338
|
+
surface,
|
|
339
|
+
wantsMic,
|
|
340
|
+
) as MediaStreamConstraints;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
// The user's chosen camera/mic devices (set in the popup, saved to storage).
|
|
@@ -1133,7 +1133,7 @@ async function acquire(message: AcquireMessage): Promise<{
|
|
|
1133
1133
|
} else {
|
|
1134
1134
|
// Native "Choose what to share" picker. This is the screenshot Steve showed.
|
|
1135
1135
|
displayStream = await navigator.mediaDevices.getDisplayMedia(
|
|
1136
|
-
displayConstraints(message.surface),
|
|
1136
|
+
displayConstraints(message.surface, message.includeMicrophone),
|
|
1137
1137
|
);
|
|
1138
1138
|
if (message.includeMicrophone) {
|
|
1139
1139
|
const audioLabel = await lookupAudioDeviceLabel(devices.audio);
|
|
@@ -1456,20 +1456,16 @@ async function saveNativeTranscript(recording: ActiveRecording): Promise<void> {
|
|
|
1456
1456
|
recording.nativeTranscript = captured.text.trim();
|
|
1457
1457
|
recording.nativeTranscriptFailure = captured.failureReason;
|
|
1458
1458
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
failureReason:
|
|
1470
|
-
recording.nativeTranscriptFailure ||
|
|
1471
|
-
"Chrome Web Speech recognition returned no transcript.",
|
|
1472
|
-
};
|
|
1459
|
+
// A partial capture must carry its reason too — the server treats text
|
|
1460
|
+
// without a reason as a complete transcript and skips the cloud fallback.
|
|
1461
|
+
const body = {
|
|
1462
|
+
recordingId: recording.recordingId,
|
|
1463
|
+
fullText: recording.nativeTranscript,
|
|
1464
|
+
source: "web-speech",
|
|
1465
|
+
...(recording.nativeTranscriptFailure
|
|
1466
|
+
? { failureReason: recording.nativeTranscriptFailure }
|
|
1467
|
+
: {}),
|
|
1468
|
+
};
|
|
1473
1469
|
const headers: Record<string, string> = {
|
|
1474
1470
|
"Content-Type": "application/json",
|
|
1475
1471
|
"X-Agent-Native-Frontend": "1",
|
|
@@ -404,11 +404,10 @@ const CAM_ON_KEY = "clips:camera-on";
|
|
|
404
404
|
const MIC_ON_KEY = "clips:mic-on";
|
|
405
405
|
const SYSTEM_AUDIO_KEY = "clips:system-audio";
|
|
406
406
|
const READINESS_REVIEWED_KEY = "clips:readiness-reviewed";
|
|
407
|
-
// The tray's rolling buffer,
|
|
408
|
-
//
|
|
409
|
-
// third-party rewind.ai app, which this app does not integrate with at all.
|
|
407
|
+
// The docs section for the tray's rolling buffer, published under the same
|
|
408
|
+
// Rewind name the settings tab uses.
|
|
410
409
|
const REWIND_DOCS_URL =
|
|
411
|
-
"https://www.agent-native.com/docs/template-clips-capture-everywhere#
|
|
410
|
+
"https://www.agent-native.com/docs/template-clips-capture-everywhere#rewind";
|
|
412
411
|
|
|
413
412
|
// Sensible defaults so the user never has to type a URL on first launch.
|
|
414
413
|
// Dev builds point at the local dev server; production builds point at the
|
|
@@ -2697,6 +2696,7 @@ export function App({
|
|
|
2697
2696
|
let stopPump: (() => void) | null = null;
|
|
2698
2697
|
let fellBackToPump = false;
|
|
2699
2698
|
let stream: MediaStream | null = null;
|
|
2699
|
+
let unlistenUnrendered: (() => void) | null = null;
|
|
2700
2700
|
|
|
2701
2701
|
const startPump = (reason: string) => {
|
|
2702
2702
|
if (cancelled || stopPump || !stream) return;
|
|
@@ -2757,11 +2757,30 @@ export function App({
|
|
|
2757
2757
|
webrtcHandle = null;
|
|
2758
2758
|
startPump(reason);
|
|
2759
2759
|
};
|
|
2760
|
+
// ICE reaching `connected` proves the transport works, nothing more.
|
|
2761
|
+
// WKWebView can refuse to play the received track (no user gesture in
|
|
2762
|
+
// the bubble page, or its window briefly had no on-screen area), and
|
|
2763
|
+
// that failure is invisible from here — so the bubble reports it and
|
|
2764
|
+
// we fall back to the pump. Without this the safety net below only
|
|
2765
|
+
// ever fired on ICE failure, which is not how this breaks in practice.
|
|
2766
|
+
listen("clips:bubble-webrtc-unrendered", (ev) => {
|
|
2767
|
+
startCanvasFallback(
|
|
2768
|
+
`bubble reported no rendered frames ${JSON.stringify(ev.payload)}`,
|
|
2769
|
+
);
|
|
2770
|
+
})
|
|
2771
|
+
.then((u) => {
|
|
2772
|
+
if (cancelled) {
|
|
2773
|
+
u();
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
unlistenUnrendered = u;
|
|
2777
|
+
})
|
|
2778
|
+
.catch(() => {});
|
|
2760
2779
|
webrtcHandle = startBubbleWebrtc({
|
|
2761
2780
|
stream: s,
|
|
2762
2781
|
onConnected: () => {
|
|
2763
2782
|
console.log(
|
|
2764
|
-
"[clips-popover] bubble WebRTC connected —
|
|
2783
|
+
"[clips-popover] bubble WebRTC transport connected — waiting for the bubble to confirm playback",
|
|
2765
2784
|
);
|
|
2766
2785
|
},
|
|
2767
2786
|
onFailure: startCanvasFallback,
|
|
@@ -2806,6 +2825,10 @@ export function App({
|
|
|
2806
2825
|
!!webrtcHandle,
|
|
2807
2826
|
!!stopPump,
|
|
2808
2827
|
);
|
|
2828
|
+
if (unlistenUnrendered) {
|
|
2829
|
+
unlistenUnrendered();
|
|
2830
|
+
unlistenUnrendered = null;
|
|
2831
|
+
}
|
|
2809
2832
|
if (webrtcHandle) {
|
|
2810
2833
|
webrtcHandle.stop();
|
|
2811
2834
|
webrtcHandle = null;
|
|
@@ -3293,11 +3316,14 @@ export function App({
|
|
|
3293
3316
|
// A restart hands off the already-live display stream from the take
|
|
3294
3317
|
// it's replacing (see `discardForRestart`/`preAcquiredDisplayStream`
|
|
3295
3318
|
// below) — it must keep recording the same screen, not re-prompt.
|
|
3296
|
-
if (
|
|
3319
|
+
if (
|
|
3320
|
+
(source === "full-screen" || source === "region") &&
|
|
3321
|
+
!options?.resumeCapture
|
|
3322
|
+
) {
|
|
3297
3323
|
try {
|
|
3298
3324
|
// Must resolve before `recordingFlowActive` flips the toolbar on
|
|
3299
|
-
// below — the toolbar
|
|
3300
|
-
// chosen screen the first time
|
|
3325
|
+
// below — the toolbar and region selector read the pick to place
|
|
3326
|
+
// themselves on the chosen screen the first time they are shown.
|
|
3301
3327
|
await pickFullscreenRecordingDisplay();
|
|
3302
3328
|
} catch (err) {
|
|
3303
3329
|
recordingFlowGateRef.current = false;
|
|
@@ -3835,22 +3861,25 @@ export function App({
|
|
|
3835
3861
|
const showCameraRow = mode !== "screen"; // screen-only has no camera
|
|
3836
3862
|
const showSourceRow = mode !== "camera"; // camera-only has no screen source
|
|
3837
3863
|
|
|
3838
|
-
const pendingUploadBanner =
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3864
|
+
const pendingUploadBanner =
|
|
3865
|
+
authStatus === "authed" ? (
|
|
3866
|
+
recordingStopFinalizing ? (
|
|
3867
|
+
<FinalizingUploadBanner />
|
|
3868
|
+
) : pendingUploads.length > 0 ? (
|
|
3869
|
+
<PendingUploadBanner
|
|
3870
|
+
uploads={pendingUploads}
|
|
3871
|
+
retryingUploadId={retryingUploadId}
|
|
3872
|
+
retryingUploadStatus={retryingUploadStatus}
|
|
3873
|
+
exportingUploadId={exportingUploadId}
|
|
3874
|
+
dismissingUploadId={dismissingUploadId}
|
|
3875
|
+
onExport={exportPendingUpload}
|
|
3876
|
+
onRetry={retryPendingUpload}
|
|
3877
|
+
onDismiss={dismissPendingUpload}
|
|
3878
|
+
onOpenFolder={openPendingUploadFolder}
|
|
3879
|
+
onConnectStorage={(upload) => openVideoStorageSetup(upload.serverUrl)}
|
|
3880
|
+
/>
|
|
3881
|
+
) : null
|
|
3882
|
+
) : null;
|
|
3854
3883
|
|
|
3855
3884
|
async function copyRewindAgentPrompt() {
|
|
3856
3885
|
try {
|