@agent-native/core 0.154.5 → 0.157.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/account-health/SKILL.md +76 -0
- package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
- package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
- package/corpus/templates/analytics/AGENTS.md +3 -0
- package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
- package/corpus/templates/analytics/actions/bigquery.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
- package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
- package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
- package/corpus/templates/assets/app/i18n-data.ts +21 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +17 -13
- package/corpus/templates/calendar/AGENTS.md +2 -0
- package/corpus/templates/calendar/DEVELOPING.md +4 -4
- package/corpus/templates/calendar/actions/get-settings.ts +4 -11
- package/corpus/templates/calendar/actions/update-availability.ts +15 -4
- package/corpus/templates/calendar/actions/update-settings.ts +15 -4
- package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
- package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
- package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
- package/corpus/templates/calendar/app/i18n-data.ts +30 -0
- package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
- package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
- package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
- package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
- package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
- package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
- package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
- package/corpus/templates/calendar/shared/api.ts +1 -0
- package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
- package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
- package/corpus/templates/calendar/shared/settings.ts +44 -0
- package/corpus/templates/clips/AGENTS.md +2 -0
- package/corpus/templates/clips/actions/add-comment.ts +5 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
- package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
- package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
- package/corpus/templates/clips/actions/update-comment.ts +9 -2
- package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
- package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +102 -59
- package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
- package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
- package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
- package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
- package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
- package/corpus/templates/clips/app/root.tsx +2 -1
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +29 -8
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +24 -6
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
- package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +116 -19
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
- package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
- package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
- package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
- package/corpus/templates/clips/desktop/src/styles.css +117 -0
- package/corpus/templates/clips/server/db/index.ts +10 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/recordings.ts +3 -1
- package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
- package/corpus/templates/clips/server/plugins/db.ts +5 -0
- package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
- package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
- package/corpus/templates/content/actions/add-comment.ts +2 -1
- package/corpus/templates/content/actions/update-comment.ts +1 -1
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
- package/corpus/templates/design/actions/generate-design.ts +10 -7
- package/corpus/templates/design/actions/present-design-variants.ts +35 -6
- package/corpus/templates/design/actions/run-design-audit.ts +33 -0
- package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
- package/corpus/templates/design/shared/design-review.ts +1 -0
- package/corpus/templates/design/shared/html-integrity.ts +66 -0
- package/corpus/templates/dispatch/.env.example +5 -0
- package/corpus/templates/dispatch/AGENTS.md +24 -19
- package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
- package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +23 -0
- package/corpus/templates/factory/actions/view-screen.ts +17 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
- package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
- package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
- package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
- package/corpus/templates/factory/app/routes/factory.tsx +41 -0
- package/corpus/templates/factory/package.json +1 -0
- package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
- package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
- package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
- package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
- package/corpus/templates/mail/vite.config.ts +7 -0
- package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
- package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
- package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
- package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
- package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
- package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
- package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +42 -2
- package/corpus/templates/slides/app/global.css +7 -40
- package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +130 -45
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/production-agent.d.ts +30 -0
- package/dist/agent/production-agent.js +105 -9
- package/dist/agent/thread-data-builder.js +17 -1
- package/dist/agent/types.d.ts +9 -0
- package/dist/cli/skills-content/help.js +6 -4
- package/dist/cli/skills.js +17 -2
- package/dist/cli/workspacify.d.ts +5 -0
- package/dist/cli/workspacify.js +65 -8
- package/dist/client/AgentPanel.d.ts +5 -0
- package/dist/client/AgentPanel.js +32 -13
- package/dist/client/AssistantChat.d.ts +36 -1
- package/dist/client/AssistantChat.js +175 -110
- package/dist/client/MultiTabAssistantChat.js +44 -27
- package/dist/client/active-run-state.d.ts +14 -1
- package/dist/client/active-run-state.js +0 -0
- package/dist/client/agent-chat-adapter.js +107 -5
- package/dist/client/analytics.js +9 -2
- package/dist/client/chat/markdown-renderer.js +4 -2
- package/dist/client/chat/message-components.d.ts +2 -2
- package/dist/client/chat/message-components.js +69 -35
- package/dist/client/chat/run-recovery.d.ts +4 -1
- package/dist/client/chat/run-recovery.js +62 -30
- package/dist/client/chat/tool-call-display.d.ts +12 -3
- package/dist/client/chat/tool-call-display.js +79 -40
- package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
- package/dist/client/chat/widgets/DataChartWidget.js +8 -2
- package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
- package/dist/client/chat/widgets/DataTableWidget.js +23 -9
- package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
- package/dist/client/chat-first/apps-rail.js +9 -4
- package/dist/client/chat-first/primary-nav.d.ts +2 -1
- package/dist/client/chat-first/primary-nav.js +3 -3
- package/dist/client/chat-first/types.d.ts +5 -1
- package/dist/client/components/ui/message-scroller.d.ts +1 -1
- package/dist/client/components/ui/message-scroller.js +7 -3
- package/dist/client/composer/runtime-adapters.js +3 -2
- package/dist/client/context-xray/ContextMeter.js +5 -1
- package/dist/client/context-xray/ContextXRayPanel.js +5 -5
- package/dist/client/error-format.d.ts +4 -0
- package/dist/client/error-format.js +93 -5
- package/dist/client/i18n.js +83 -17
- package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
- package/dist/client/markdown/InlineMarkdown.js +65 -0
- package/dist/client/markdown/index.d.ts +1 -0
- package/dist/client/markdown/index.js +1 -0
- package/dist/client/observability/ThumbsFeedback.js +10 -8
- package/dist/client/review/ReviewThreadPanel.js +2 -1
- package/dist/client/run-stream-ownership.d.ts +39 -0
- package/dist/client/run-stream-ownership.js +65 -0
- package/dist/client/sharing/ShareButton.js +160 -54
- package/dist/coding-tools/run-code.js +2 -1
- package/dist/coding-tools/sandbox/background.d.ts +1 -0
- package/dist/coding-tools/sandbox/background.js +8 -1
- package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
- package/dist/coding-tools/sandbox/executions-store.js +41 -3
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/feature-flags/index.d.ts +1 -1
- package/dist/feature-flags/index.js +1 -1
- package/dist/feature-flags/store.d.ts +10 -0
- package/dist/feature-flags/store.js +112 -1
- package/dist/guards/no-env-credentials.js +4 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/integrations/adapters/slack.js +5 -1
- package/dist/localization/core-messages/ar-SA.d.ts +3 -0
- package/dist/localization/core-messages/ar-SA.js +541 -0
- package/dist/localization/core-messages/de-DE.d.ts +3 -0
- package/dist/localization/core-messages/de-DE.js +513 -0
- package/dist/localization/core-messages/en-US.d.ts +513 -0
- package/dist/localization/core-messages/en-US.js +513 -0
- package/dist/localization/core-messages/es-ES.d.ts +3 -0
- package/dist/localization/core-messages/es-ES.js +520 -0
- package/dist/localization/core-messages/fr-FR.d.ts +3 -0
- package/dist/localization/core-messages/fr-FR.js +520 -0
- package/dist/localization/core-messages/hi-IN.d.ts +3 -0
- package/dist/localization/core-messages/hi-IN.js +513 -0
- package/dist/localization/core-messages/ja-JP.d.ts +3 -0
- package/dist/localization/core-messages/ja-JP.js +506 -0
- package/dist/localization/core-messages/ko-KR.d.ts +3 -0
- package/dist/localization/core-messages/ko-KR.js +506 -0
- package/dist/localization/core-messages/pt-BR.d.ts +3 -0
- package/dist/localization/core-messages/pt-BR.js +520 -0
- package/dist/localization/core-messages/zh-CN.d.ts +3 -0
- package/dist/localization/core-messages/zh-CN.js +506 -0
- package/dist/localization/core-messages/zh-TW.d.ts +3 -0
- package/dist/localization/core-messages/zh-TW.js +506 -0
- package/dist/localization/core-messages.d.ts +524 -0
- package/dist/localization/core-messages.js +124 -0
- package/dist/localization/default-messages.d.ts +3 -0
- package/dist/localization/default-messages.js +3 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/resources/agents.js +40 -5
- package/dist/resources/metadata.d.ts +10 -0
- package/dist/resources/metadata.js +7 -3
- package/dist/review/actions/create-review-comment.js +1 -1
- package/dist/review/actions/reply-review-comment.js +1 -1
- package/dist/review/actions/resolve-review-thread.js +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/action-routes.js +32 -6
- package/dist/server/agent-chat/plugin-options.d.ts +7 -2
- package/dist/server/agent-chat-plugin.d.ts +10 -0
- package/dist/server/agent-chat-plugin.js +144 -41
- package/dist/server/agent-discovery.js +14 -3
- package/dist/server/agent-run-context.d.ts +7 -1
- package/dist/server/agent-run-context.js +9 -2
- package/dist/server/agent-teams-run-queue.d.ts +3 -0
- package/dist/server/agent-teams.js +38 -9
- package/dist/server/core-routes-plugin.js +1 -1
- package/dist/server/identity-sso-migrations.d.ts +7 -0
- package/dist/server/identity-sso-migrations.js +36 -0
- package/dist/server/identity-sso-store.d.ts +3 -2
- package/dist/server/identity-sso-store.js +8 -3
- package/dist/server/index.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +4 -0
- package/dist/server/request-context.d.ts +7 -0
- package/dist/server/request-context.js +15 -0
- package/dist/tracking/providers.js +13 -3
- package/dist/workspace-files/tool.js +1 -1
- package/docs/content/agent-surfaces.mdx +30 -0
- package/package.json +3 -2
|
@@ -66,7 +66,10 @@ import { UpdateBanner } from "./components/UpdateBanner";
|
|
|
66
66
|
import { useMediaDevices } from "./hooks/useMediaDevices";
|
|
67
67
|
import { useMeetingTranscription } from "./hooks/useMeetingTranscription";
|
|
68
68
|
import { stopAllMicMeters } from "./hooks/useMicMeter";
|
|
69
|
-
import {
|
|
69
|
+
import {
|
|
70
|
+
useWhisperSettings,
|
|
71
|
+
type WhisperModelOption,
|
|
72
|
+
} from "./hooks/useWhisperSettings";
|
|
70
73
|
import { startBubbleFramePump } from "./lib/bubble-pump";
|
|
71
74
|
import { shouldKeepBubbleSession } from "./lib/bubble-session";
|
|
72
75
|
import {
|
|
@@ -111,6 +114,10 @@ import {
|
|
|
111
114
|
} from "./lib/recording-link";
|
|
112
115
|
import { REWIND_AGENT_PROMPT } from "./lib/rewind-agent-prompt";
|
|
113
116
|
import { getRewindStatusPresentation } from "./lib/rewind-status";
|
|
117
|
+
import {
|
|
118
|
+
initialDesktopSettingsTab,
|
|
119
|
+
type DesktopSettingsTab,
|
|
120
|
+
} from "./lib/settings-navigation";
|
|
114
121
|
import {
|
|
115
122
|
loadBool,
|
|
116
123
|
loadString,
|
|
@@ -133,6 +140,7 @@ import {
|
|
|
133
140
|
type VoiceProvider,
|
|
134
141
|
type VoiceShortcutPreference,
|
|
135
142
|
} from "./lib/voice-dictation";
|
|
143
|
+
import { whisperModelOptionLabel } from "./lib/whisper-model-picker";
|
|
136
144
|
import {
|
|
137
145
|
useFeatureConfig,
|
|
138
146
|
type FeatureConfig,
|
|
@@ -168,7 +176,7 @@ type PopoverView =
|
|
|
168
176
|
| "meetings"
|
|
169
177
|
| "dictation";
|
|
170
178
|
|
|
171
|
-
type SettingsTabId =
|
|
179
|
+
type SettingsTabId = DesktopSettingsTab;
|
|
172
180
|
|
|
173
181
|
interface PopoverMeeting {
|
|
174
182
|
id: string;
|
|
@@ -988,6 +996,14 @@ export function App() {
|
|
|
988
996
|
const [shareLinkNotice, setShareLinkNotice] =
|
|
989
997
|
useState<ShareLinkNotice | null>(null);
|
|
990
998
|
const [popoverView, setPopoverView] = useState<PopoverView>("recorder");
|
|
999
|
+
const [initialSettingsTab, setInitialSettingsTab] =
|
|
1000
|
+
useState<SettingsTabId>("general");
|
|
1001
|
+
|
|
1002
|
+
function openSettings(tab: SettingsTabId = "general") {
|
|
1003
|
+
setInitialSettingsTab(tab);
|
|
1004
|
+
setPopoverView("settings");
|
|
1005
|
+
}
|
|
1006
|
+
|
|
991
1007
|
const [rewindSettingsReturnView, setRewindSettingsReturnView] = useState<
|
|
992
1008
|
"recorder" | "settings"
|
|
993
1009
|
>("recorder");
|
|
@@ -3802,6 +3818,7 @@ export function App() {
|
|
|
3802
3818
|
{pendingUploadBanner}
|
|
3803
3819
|
{isRecording ? <ActiveRecordingBanner /> : null}
|
|
3804
3820
|
<Setup
|
|
3821
|
+
initialSettingsTab={initialSettingsTab}
|
|
3805
3822
|
recordingActive={isRecording || recordingFlowActive}
|
|
3806
3823
|
initial={serverUrl}
|
|
3807
3824
|
serverUrl={serverUrl}
|
|
@@ -3855,7 +3872,7 @@ export function App() {
|
|
|
3855
3872
|
onOpenMeeting={(meetingId) =>
|
|
3856
3873
|
openInBrowser(`/meetings/${encodeURIComponent(meetingId)}`)
|
|
3857
3874
|
}
|
|
3858
|
-
onOpenSettings={() =>
|
|
3875
|
+
onOpenSettings={() => openSettings()}
|
|
3859
3876
|
onStartNotes={startMeetingNotes}
|
|
3860
3877
|
onStartNotesAndJoin={startMeetingNotesAndJoin}
|
|
3861
3878
|
onShowActiveMeeting={showActiveMeetingPill}
|
|
@@ -3877,7 +3894,7 @@ export function App() {
|
|
|
3877
3894
|
voiceProvider={voiceProvider}
|
|
3878
3895
|
onBack={() => setPopoverView("recorder")}
|
|
3879
3896
|
onOpenDictate={() => openInBrowser("/dictate")}
|
|
3880
|
-
onOpenSettings={() =>
|
|
3897
|
+
onOpenSettings={() => openSettings("dictation")}
|
|
3881
3898
|
/>
|
|
3882
3899
|
</div>
|
|
3883
3900
|
);
|
|
@@ -3932,7 +3949,7 @@ export function App() {
|
|
|
3932
3949
|
</>
|
|
3933
3950
|
)}
|
|
3934
3951
|
<div className="footer">
|
|
3935
|
-
<a className="footer-link" onClick={() =>
|
|
3952
|
+
<a className="footer-link" onClick={() => openSettings()}>
|
|
3936
3953
|
Settings
|
|
3937
3954
|
</a>
|
|
3938
3955
|
</div>
|
|
@@ -4154,7 +4171,7 @@ export function App() {
|
|
|
4154
4171
|
<BottomButton
|
|
4155
4172
|
icon="settings"
|
|
4156
4173
|
label="Settings"
|
|
4157
|
-
onClick={() =>
|
|
4174
|
+
onClick={() => openSettings()}
|
|
4158
4175
|
/>
|
|
4159
4176
|
</div>
|
|
4160
4177
|
</div>
|
|
@@ -5296,6 +5313,7 @@ function desktopUpdateStatusText(status: UpdateStatus): string {
|
|
|
5296
5313
|
|
|
5297
5314
|
function Setup({
|
|
5298
5315
|
surface = "settings",
|
|
5316
|
+
initialSettingsTab,
|
|
5299
5317
|
promptRewindEnable = false,
|
|
5300
5318
|
recordingActive = false,
|
|
5301
5319
|
initial,
|
|
@@ -5324,6 +5342,7 @@ function Setup({
|
|
|
5324
5342
|
onSignOut,
|
|
5325
5343
|
}: {
|
|
5326
5344
|
surface?: "settings" | "memory" | "rewind";
|
|
5345
|
+
initialSettingsTab?: SettingsTabId;
|
|
5327
5346
|
promptRewindEnable?: boolean;
|
|
5328
5347
|
recordingActive?: boolean;
|
|
5329
5348
|
initial?: string | null;
|
|
@@ -5353,7 +5372,9 @@ function Setup({
|
|
|
5353
5372
|
}) {
|
|
5354
5373
|
const [url, setUrl] = useState(initial ?? DEFAULT_URL);
|
|
5355
5374
|
const [readinessOpen, setReadinessOpen] = useState(false);
|
|
5356
|
-
const [settingsTab, setSettingsTab] = useState<SettingsTabId>(
|
|
5375
|
+
const [settingsTab, setSettingsTab] = useState<SettingsTabId>(() =>
|
|
5376
|
+
initialDesktopSettingsTab(initialSettingsTab),
|
|
5377
|
+
);
|
|
5357
5378
|
const [rewindSettingsOpen, setRewindSettingsOpen] = useState(false);
|
|
5358
5379
|
const [rewindActivityOpen, setRewindActivityOpen] = useState(false);
|
|
5359
5380
|
const [rewindMemoryOpen, setRewindMemoryOpen] = useState(false);
|
|
@@ -5361,6 +5382,12 @@ function Setup({
|
|
|
5361
5382
|
const [rewindPrivacyOpen, setRewindPrivacyOpen] = useState(false);
|
|
5362
5383
|
const [rewindAgentSetupOpen, setRewindAgentSetupOpen] = useState(false);
|
|
5363
5384
|
const [rewindHandoffOpen, setRewindHandoffOpen] = useState(false);
|
|
5385
|
+
|
|
5386
|
+
useEffect(() => {
|
|
5387
|
+
if (surface !== "settings") return;
|
|
5388
|
+
setSettingsTab(initialDesktopSettingsTab(initialSettingsTab));
|
|
5389
|
+
}, [initialSettingsTab, surface]);
|
|
5390
|
+
|
|
5364
5391
|
const featureConfig = useFeatureConfig();
|
|
5365
5392
|
const updateStatus = useUpdateStatus();
|
|
5366
5393
|
const voiceEnabled = featureConfig?.voiceEnabled !== false;
|
|
@@ -7491,22 +7518,15 @@ function Setup({
|
|
|
7491
7518
|
"Choose the downloaded model used for offline transcription."
|
|
7492
7519
|
}
|
|
7493
7520
|
control={
|
|
7494
|
-
<
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
onChange={(event) => whisper.setModelId(event.target.value)}
|
|
7521
|
+
<WhisperModelPicker
|
|
7522
|
+
models={whisperModels}
|
|
7523
|
+
modelId={whisperModelId}
|
|
7524
|
+
onChange={whisper.setModelId}
|
|
7499
7525
|
disabled={
|
|
7500
7526
|
whisperModels.length === 0 ||
|
|
7501
7527
|
whisperStatus?.state === "downloading"
|
|
7502
7528
|
}
|
|
7503
|
-
|
|
7504
|
-
{whisperModels.map((model) => (
|
|
7505
|
-
<option key={model.id} value={model.id}>
|
|
7506
|
-
{model.title} · {model.sizeMb} MB — {model.description}
|
|
7507
|
-
</option>
|
|
7508
|
-
))}
|
|
7509
|
-
</select>
|
|
7529
|
+
/>
|
|
7510
7530
|
}
|
|
7511
7531
|
>
|
|
7512
7532
|
<>
|
|
@@ -8061,6 +8081,83 @@ function SettingLabel({
|
|
|
8061
8081
|
);
|
|
8062
8082
|
}
|
|
8063
8083
|
|
|
8084
|
+
function WhisperModelPicker({
|
|
8085
|
+
models,
|
|
8086
|
+
modelId,
|
|
8087
|
+
onChange,
|
|
8088
|
+
disabled,
|
|
8089
|
+
}: {
|
|
8090
|
+
models: WhisperModelOption[];
|
|
8091
|
+
modelId: string;
|
|
8092
|
+
onChange: (modelId: string) => void;
|
|
8093
|
+
disabled: boolean;
|
|
8094
|
+
}) {
|
|
8095
|
+
const [open, setOpen] = useState(false);
|
|
8096
|
+
const selectedModel = models.find((model) => model.id === modelId);
|
|
8097
|
+
|
|
8098
|
+
return (
|
|
8099
|
+
<PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
|
|
8100
|
+
<PopoverPrimitive.Trigger asChild>
|
|
8101
|
+
<button
|
|
8102
|
+
type="button"
|
|
8103
|
+
className="whisper-model-picker-trigger"
|
|
8104
|
+
disabled={disabled}
|
|
8105
|
+
aria-haspopup="listbox"
|
|
8106
|
+
aria-expanded={open}
|
|
8107
|
+
>
|
|
8108
|
+
<span className="whisper-model-picker-trigger-label">
|
|
8109
|
+
{selectedModel
|
|
8110
|
+
? whisperModelOptionLabel(selectedModel)
|
|
8111
|
+
: "No models available"}
|
|
8112
|
+
</span>
|
|
8113
|
+
<IconChevronDown size={15} stroke={1.8} aria-hidden="true" />
|
|
8114
|
+
</button>
|
|
8115
|
+
</PopoverPrimitive.Trigger>
|
|
8116
|
+
<PopoverPrimitive.Portal>
|
|
8117
|
+
<PopoverPrimitive.Content
|
|
8118
|
+
side="bottom"
|
|
8119
|
+
align="end"
|
|
8120
|
+
sideOffset={6}
|
|
8121
|
+
collisionPadding={12}
|
|
8122
|
+
className="whisper-model-picker"
|
|
8123
|
+
data-popover-overlay="true"
|
|
8124
|
+
>
|
|
8125
|
+
<div
|
|
8126
|
+
className="whisper-model-picker-list"
|
|
8127
|
+
role="listbox"
|
|
8128
|
+
aria-label="Whisper model"
|
|
8129
|
+
>
|
|
8130
|
+
{models.map((model) => {
|
|
8131
|
+
const selected = model.id === modelId;
|
|
8132
|
+
return (
|
|
8133
|
+
<button
|
|
8134
|
+
key={model.id}
|
|
8135
|
+
type="button"
|
|
8136
|
+
role="option"
|
|
8137
|
+
aria-selected={selected}
|
|
8138
|
+
className={`whisper-model-picker-option ${selected ? "is-selected" : ""}`}
|
|
8139
|
+
onClick={() => {
|
|
8140
|
+
onChange(model.id);
|
|
8141
|
+
setOpen(false);
|
|
8142
|
+
}}
|
|
8143
|
+
>
|
|
8144
|
+
<span className="whisper-model-picker-option-title">
|
|
8145
|
+
<span>{whisperModelOptionLabel(model)}</span>
|
|
8146
|
+
{selected ? <IconCheck size={15} stroke={2.1} /> : null}
|
|
8147
|
+
</span>
|
|
8148
|
+
<span className="whisper-model-picker-option-description">
|
|
8149
|
+
{model.description}
|
|
8150
|
+
</span>
|
|
8151
|
+
</button>
|
|
8152
|
+
);
|
|
8153
|
+
})}
|
|
8154
|
+
</div>
|
|
8155
|
+
</PopoverPrimitive.Content>
|
|
8156
|
+
</PopoverPrimitive.Portal>
|
|
8157
|
+
</PopoverPrimitive.Root>
|
|
8158
|
+
);
|
|
8159
|
+
}
|
|
8160
|
+
|
|
8064
8161
|
function WhisperModelStatusRow({
|
|
8065
8162
|
status,
|
|
8066
8163
|
enabled,
|
|
@@ -83,6 +83,10 @@ import {
|
|
|
83
83
|
type PauseTransitionQueue,
|
|
84
84
|
} from "./pause-transition";
|
|
85
85
|
import { reconcileProcessingBackup } from "./processing-backup-recovery";
|
|
86
|
+
import {
|
|
87
|
+
buildCreateRecordingRequestBody,
|
|
88
|
+
type NativeRecordingRequestOptions,
|
|
89
|
+
} from "./recording-request";
|
|
86
90
|
import {
|
|
87
91
|
guardRecordingStart,
|
|
88
92
|
RECORDING_START_TIMEOUT_MS,
|
|
@@ -143,7 +147,6 @@ const STREAM_CHUNK_BYTES = 15 * GCS_CHUNK_ALIGN_BYTES; // 3.75 MiB
|
|
|
143
147
|
// - "streaming" — server has a resumable session; flush aligned chunks live.
|
|
144
148
|
// - "buffered" — per-blob chunks staged server-side, assembled on finalize.
|
|
145
149
|
type UploadMode = "streaming" | "buffered";
|
|
146
|
-
type StreamingUploadClient = "desktop-native";
|
|
147
150
|
const CLOUD_CAPTURE_FRAME_RATE = 24;
|
|
148
151
|
const CLOUD_CAPTURE_MAX_WIDTH = 1920;
|
|
149
152
|
const CLOUD_CAPTURE_MAX_HEIGHT = 1080;
|
|
@@ -1538,13 +1541,7 @@ async function createServerRecording(
|
|
|
1538
1541
|
hasCamera: boolean,
|
|
1539
1542
|
hasAudio: boolean,
|
|
1540
1543
|
titleContext?: CaptureTitleResult,
|
|
1541
|
-
options?: {
|
|
1542
|
-
mimeType?: string;
|
|
1543
|
-
requestStreaming?: boolean;
|
|
1544
|
-
streamingUploadClient?: StreamingUploadClient;
|
|
1545
|
-
visibility?: "public" | "private";
|
|
1546
|
-
signal?: AbortSignal;
|
|
1547
|
-
},
|
|
1544
|
+
options?: NativeRecordingRequestOptions & { signal?: AbortSignal },
|
|
1548
1545
|
) {
|
|
1549
1546
|
const url = `${serverUrl.replace(/\/+$/, "")}/_agent-native/actions/create-recording`;
|
|
1550
1547
|
console.log("[clips-recorder] POST", url, {
|
|
@@ -1564,27 +1561,14 @@ async function createServerRecording(
|
|
|
1564
1561
|
// cookies aren't needed.
|
|
1565
1562
|
credentials: "include",
|
|
1566
1563
|
signal: options?.signal,
|
|
1567
|
-
body: JSON.stringify(
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
mimeType: options.mimeType,
|
|
1576
|
-
streamingUploadClient: options.streamingUploadClient,
|
|
1577
|
-
}
|
|
1578
|
-
: {}),
|
|
1579
|
-
...(titleContext
|
|
1580
|
-
? {
|
|
1581
|
-
title: titleContext.title,
|
|
1582
|
-
titleSource: titleContext.titleSource,
|
|
1583
|
-
sourceAppName: titleContext.sourceAppName,
|
|
1584
|
-
sourceWindowTitle: titleContext.sourceWindowTitle,
|
|
1585
|
-
}
|
|
1586
|
-
: {}),
|
|
1587
|
-
}),
|
|
1564
|
+
body: JSON.stringify(
|
|
1565
|
+
buildCreateRecordingRequestBody(
|
|
1566
|
+
hasCamera,
|
|
1567
|
+
hasAudio,
|
|
1568
|
+
titleContext,
|
|
1569
|
+
options,
|
|
1570
|
+
),
|
|
1571
|
+
),
|
|
1588
1572
|
});
|
|
1589
1573
|
} catch (err) {
|
|
1590
1574
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CaptureTitleResult } from "./recording-title";
|
|
2
|
+
|
|
3
|
+
export type NativeRecordingVisibility = "private" | "org" | "public";
|
|
4
|
+
|
|
5
|
+
export interface NativeRecordingRequestOptions {
|
|
6
|
+
mimeType?: string;
|
|
7
|
+
requestStreaming?: boolean;
|
|
8
|
+
streamingUploadClient?: "desktop-native";
|
|
9
|
+
visibility?: NativeRecordingVisibility;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function buildCreateRecordingRequestBody(
|
|
13
|
+
hasCamera: boolean,
|
|
14
|
+
hasAudio: boolean,
|
|
15
|
+
titleContext?: CaptureTitleResult,
|
|
16
|
+
options?: NativeRecordingRequestOptions,
|
|
17
|
+
): Record<string, unknown> {
|
|
18
|
+
return {
|
|
19
|
+
hasCamera,
|
|
20
|
+
hasAudio,
|
|
21
|
+
spaceIds: [],
|
|
22
|
+
...(options?.visibility ? { visibility: options.visibility } : {}),
|
|
23
|
+
...(options?.requestStreaming
|
|
24
|
+
? {
|
|
25
|
+
requestStreaming: true,
|
|
26
|
+
mimeType: options.mimeType,
|
|
27
|
+
streamingUploadClient: options.streamingUploadClient,
|
|
28
|
+
}
|
|
29
|
+
: {}),
|
|
30
|
+
...(titleContext
|
|
31
|
+
? {
|
|
32
|
+
title: titleContext.title,
|
|
33
|
+
titleSource: titleContext.titleSource,
|
|
34
|
+
sourceAppName: titleContext.sourceAppName,
|
|
35
|
+
sourceWindowTitle: titleContext.sourceWindowTitle,
|
|
36
|
+
}
|
|
37
|
+
: {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -362,6 +362,10 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
362
362
|
width: min(100%, 300px);
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
.settings-row-control > .whisper-model-picker-trigger {
|
|
366
|
+
width: min(100%, 300px);
|
|
367
|
+
}
|
|
368
|
+
|
|
365
369
|
.settings-row-control > .setup-textarea {
|
|
366
370
|
width: 100%;
|
|
367
371
|
}
|
|
@@ -396,6 +400,119 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
396
400
|
white-space: nowrap;
|
|
397
401
|
}
|
|
398
402
|
|
|
403
|
+
.whisper-model-picker-trigger {
|
|
404
|
+
display: flex;
|
|
405
|
+
align-items: center;
|
|
406
|
+
justify-content: space-between;
|
|
407
|
+
gap: 8px;
|
|
408
|
+
min-width: 0;
|
|
409
|
+
padding: 10px 12px;
|
|
410
|
+
border: 1px solid var(--border-strong);
|
|
411
|
+
border-radius: var(--radius-sm);
|
|
412
|
+
background: var(--bg);
|
|
413
|
+
color: var(--fg);
|
|
414
|
+
cursor: pointer;
|
|
415
|
+
font-size: 13px;
|
|
416
|
+
text-align: left;
|
|
417
|
+
outline: none;
|
|
418
|
+
transition:
|
|
419
|
+
border-color 120ms,
|
|
420
|
+
box-shadow 120ms;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.whisper-model-picker-trigger:hover:not(:disabled) {
|
|
424
|
+
border-color: var(--fg-muted);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.whisper-model-picker-trigger:focus-visible {
|
|
428
|
+
border-color: var(--brand);
|
|
429
|
+
box-shadow: 0 0 0 3px var(--brand-ring);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.whisper-model-picker-trigger:disabled {
|
|
433
|
+
opacity: 0.48;
|
|
434
|
+
background: var(--surface);
|
|
435
|
+
cursor: not-allowed;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.whisper-model-picker-trigger-label {
|
|
439
|
+
min-width: 0;
|
|
440
|
+
overflow: hidden;
|
|
441
|
+
text-overflow: ellipsis;
|
|
442
|
+
white-space: nowrap;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.whisper-model-picker {
|
|
446
|
+
z-index: 80;
|
|
447
|
+
width: min(420px, calc(100vw - 24px));
|
|
448
|
+
max-height: min(360px, calc(100vh - 24px));
|
|
449
|
+
overflow-x: hidden;
|
|
450
|
+
overflow-y: auto;
|
|
451
|
+
padding: 6px;
|
|
452
|
+
border: 1px solid var(--border);
|
|
453
|
+
border-radius: var(--radius);
|
|
454
|
+
background: var(--bg);
|
|
455
|
+
color: var(--fg);
|
|
456
|
+
box-shadow: var(--shadow-md);
|
|
457
|
+
outline: none;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.whisper-model-picker-list {
|
|
461
|
+
display: grid;
|
|
462
|
+
gap: 2px;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.whisper-model-picker-option {
|
|
466
|
+
display: grid;
|
|
467
|
+
gap: 4px;
|
|
468
|
+
width: 100%;
|
|
469
|
+
min-width: 0;
|
|
470
|
+
padding: 9px 10px;
|
|
471
|
+
border: 1px solid transparent;
|
|
472
|
+
border-radius: var(--radius-sm);
|
|
473
|
+
background: transparent;
|
|
474
|
+
color: var(--fg-muted);
|
|
475
|
+
cursor: pointer;
|
|
476
|
+
text-align: left;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.whisper-model-picker-option:hover,
|
|
480
|
+
.whisper-model-picker-option.is-selected {
|
|
481
|
+
background: var(--surface-hover);
|
|
482
|
+
color: var(--fg);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.whisper-model-picker-option.is-selected {
|
|
486
|
+
border-color: var(--border);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.whisper-model-picker-option:focus-visible {
|
|
490
|
+
outline: none;
|
|
491
|
+
box-shadow: 0 0 0 3px var(--brand-ring);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.whisper-model-picker-option-title {
|
|
495
|
+
display: flex;
|
|
496
|
+
align-items: center;
|
|
497
|
+
justify-content: space-between;
|
|
498
|
+
gap: 10px;
|
|
499
|
+
min-width: 0;
|
|
500
|
+
color: var(--fg);
|
|
501
|
+
font-size: 12px;
|
|
502
|
+
font-weight: 650;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.whisper-model-picker-option-title > span {
|
|
506
|
+
min-width: 0;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.whisper-model-picker-option-description {
|
|
510
|
+
color: var(--fg-muted);
|
|
511
|
+
font-size: 11px;
|
|
512
|
+
line-height: 1.4;
|
|
513
|
+
white-space: normal;
|
|
514
|
+
}
|
|
515
|
+
|
|
399
516
|
.settings-url-input {
|
|
400
517
|
width: 100%;
|
|
401
518
|
}
|
|
@@ -3,6 +3,10 @@ import { organizations } from "@agent-native/core/org";
|
|
|
3
3
|
import { registerShareableResource } from "@agent-native/core/sharing";
|
|
4
4
|
import { eq } from "drizzle-orm";
|
|
5
5
|
|
|
6
|
+
import {
|
|
7
|
+
CLIPS_MEETING_AGENT_CONTEXT_ENDPOINT,
|
|
8
|
+
CLIPS_MEETING_AGENT_RESOURCE_KIND,
|
|
9
|
+
} from "../../shared/meeting-agent-access.js";
|
|
6
10
|
import {
|
|
7
11
|
absoluteUrl,
|
|
8
12
|
recordingShareEmailExtras,
|
|
@@ -75,6 +79,12 @@ registerShareableResource({
|
|
|
75
79
|
displayName: "Meeting",
|
|
76
80
|
titleColumn: "title",
|
|
77
81
|
getResourcePath: (meeting) => `/meetings/${meeting.id}`,
|
|
82
|
+
agentReadable: {
|
|
83
|
+
resourceKind: CLIPS_MEETING_AGENT_RESOURCE_KIND,
|
|
84
|
+
getContextPath: () => CLIPS_MEETING_AGENT_CONTEXT_ENDPOINT,
|
|
85
|
+
getPagePath: (meeting) =>
|
|
86
|
+
`/share/meeting/${encodeURIComponent(meeting.id)}`,
|
|
87
|
+
},
|
|
78
88
|
getDb,
|
|
79
89
|
});
|
|
80
90
|
|
|
@@ -216,6 +216,7 @@ export const recordings = table("recordings", {
|
|
|
216
216
|
|
|
217
217
|
createdAt: text("created_at").notNull().default(now()),
|
|
218
218
|
updatedAt: text("updated_at").notNull().default(now()),
|
|
219
|
+
mediaUpdatedAt: text("media_updated_at").notNull().default(now()),
|
|
219
220
|
archivedAt: text("archived_at"),
|
|
220
221
|
trashedAt: text("trashed_at"),
|
|
221
222
|
|
|
@@ -11,6 +11,7 @@ import { HTTPError, type H3Event } from "h3";
|
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
CLIPS_USER_PREFS_KEY,
|
|
14
|
+
DEFAULT_CLIPS_RECORDING_VISIBILITY,
|
|
14
15
|
type ClipsUserPrefs,
|
|
15
16
|
} from "../../shared/clips-ai-prefs.js";
|
|
16
17
|
import { getDb, schema } from "../db/index.js";
|
|
@@ -67,7 +68,8 @@ export type OrganizationAccessRole = "owner" | "admin" | "member";
|
|
|
67
68
|
|
|
68
69
|
export type RecordingVisibility = "private" | "org" | "public";
|
|
69
70
|
|
|
70
|
-
export const DEFAULT_RECORDING_VISIBILITY: RecordingVisibility =
|
|
71
|
+
export const DEFAULT_RECORDING_VISIBILITY: RecordingVisibility =
|
|
72
|
+
DEFAULT_CLIPS_RECORDING_VISIBILITY;
|
|
71
73
|
|
|
72
74
|
export function isRecordingVisibility(
|
|
73
75
|
value: unknown,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appStateGet,
|
|
3
|
+
deleteAppState,
|
|
4
|
+
writeAppState,
|
|
5
|
+
} from "@agent-native/core/application-state";
|
|
6
|
+
|
|
7
|
+
export const SEEKABLE_REPAIR_STATE_PREFIX = "recording-seekable-repair-";
|
|
8
|
+
export const SEEKABLE_REPAIR_PENDING_TTL_MS = 10 * 60 * 1000;
|
|
9
|
+
|
|
10
|
+
export type SeekableRepairMarker = {
|
|
11
|
+
recordingId: string;
|
|
12
|
+
status: "pending";
|
|
13
|
+
videoUrl: string;
|
|
14
|
+
startedAt: string;
|
|
15
|
+
expiresAt: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function seekableRepairStateKey(recordingId: string): string {
|
|
19
|
+
return `${SEEKABLE_REPAIR_STATE_PREFIX}${recordingId}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function parseSeekableRepairMarker(
|
|
23
|
+
value: unknown,
|
|
24
|
+
): SeekableRepairMarker | null {
|
|
25
|
+
if (!value || typeof value !== "object") return null;
|
|
26
|
+
const state = value as Record<string, unknown>;
|
|
27
|
+
if (
|
|
28
|
+
typeof state.recordingId !== "string" ||
|
|
29
|
+
!state.recordingId ||
|
|
30
|
+
state.status !== "pending" ||
|
|
31
|
+
typeof state.videoUrl !== "string" ||
|
|
32
|
+
!state.videoUrl ||
|
|
33
|
+
typeof state.startedAt !== "string" ||
|
|
34
|
+
!Number.isFinite(Date.parse(state.startedAt)) ||
|
|
35
|
+
typeof state.expiresAt !== "string" ||
|
|
36
|
+
!Number.isFinite(Date.parse(state.expiresAt))
|
|
37
|
+
) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return state as SeekableRepairMarker;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function markSeekableRepairPending(params: {
|
|
44
|
+
recordingId: string;
|
|
45
|
+
videoUrl: string;
|
|
46
|
+
}): Promise<void> {
|
|
47
|
+
const startedAt = Date.now();
|
|
48
|
+
await writeAppState(seekableRepairStateKey(params.recordingId), {
|
|
49
|
+
recordingId: params.recordingId,
|
|
50
|
+
status: "pending",
|
|
51
|
+
videoUrl: params.videoUrl,
|
|
52
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
53
|
+
expiresAt: new Date(
|
|
54
|
+
startedAt + SEEKABLE_REPAIR_PENDING_TTL_MS,
|
|
55
|
+
).toISOString(),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function clearSeekableRepairPending(
|
|
60
|
+
recordingId: string,
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
await deleteAppState(seekableRepairStateKey(recordingId));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function isSeekableRepairPending(args: {
|
|
66
|
+
ownerEmail: string;
|
|
67
|
+
recordingId: string;
|
|
68
|
+
recordingStatus: string;
|
|
69
|
+
videoUrl?: string | null;
|
|
70
|
+
}): Promise<boolean> {
|
|
71
|
+
if (args.recordingStatus !== "ready") return false;
|
|
72
|
+
|
|
73
|
+
const value = await appStateGet(
|
|
74
|
+
args.ownerEmail,
|
|
75
|
+
seekableRepairStateKey(args.recordingId),
|
|
76
|
+
);
|
|
77
|
+
if (value === null) return false;
|
|
78
|
+
const marker = parseSeekableRepairMarker(value);
|
|
79
|
+
if (!marker) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Malformed seekable repair marker for recording ${args.recordingId}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (marker.recordingId !== args.recordingId) return false;
|
|
85
|
+
if (args.videoUrl !== undefined && marker.videoUrl !== args.videoUrl) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return Date.parse(marker.expiresAt) > Date.now();
|
|
90
|
+
}
|
|
@@ -1154,6 +1154,11 @@ export const migrations = runMigrations(
|
|
|
1154
1154
|
// re-derived by regex over the message.
|
|
1155
1155
|
sql: `ALTER TABLE recording_transcripts ADD COLUMN failure_code TEXT`,
|
|
1156
1156
|
},
|
|
1157
|
+
{
|
|
1158
|
+
version: 65,
|
|
1159
|
+
name: "recording-media-updated-at",
|
|
1160
|
+
sql: `ALTER TABLE recordings ADD COLUMN IF NOT EXISTS media_updated_at TEXT NOT NULL DEFAULT (datetime('now'))`,
|
|
1161
|
+
},
|
|
1157
1162
|
],
|
|
1158
1163
|
{ table: "clips_migrations" },
|
|
1159
1164
|
);
|