From f13bde1e035b0c4c9376b07cdbe88b81edb11707 Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Tue, 14 Jul 2026 01:51:39 +0530 Subject: [PATCH] Align call recorder vocabulary with core dialect (#22831) Part 1/5 of splitting #22739 into a reviewable stack. Mechanical renames only, no behavior change: - `ingestion` -> `import` across data/domain/flows (`completeCallRecordingIngestion` -> `completeCallRecordingImport`, `ingestCallRecordingMedia` -> `importCallRecordingMedia`, `reconcileCallRecordingTranscriptArtifact` -> `importCallRecordingTranscript`, ...) - `reapOrphanedCallRecorders` -> `cleanupOrphanedRecallBots` - `ensureCallRecorder` -> `scheduleRecallBotForCallRecording`, `healCallRecordingsMissingBot` -> `scheduleRecallBotsForPendingCallRecordings` - `extractRecallBotConvergence` -> `extractRecallBotSyncState` - formatting drift in touched files Next in the stack: Recall API hardening, billing charge verification, divergence-scoped sync crons, webhook artifact continuation. Review in cubic --- .../components/TranscriptEntryListItem.tsx | 4 +- ...call-recorder-recording-retention-hours.ts | 2 +- ...=> complete-call-recording-import.test.ts} | 10 ++-- ...=> complete-call-recording-import.util.ts} | 2 +- ...-call-recording-summaries-backfill.util.ts | 11 +++-- ...ract-conference-link-url-from-text.test.ts | 5 +- ...is-call-recording-import-complete.test.ts} | 24 +++++----- ...ld-complete-call-recording-import.test.ts} | 20 ++++---- .../domain/build-bot-image.util.ts | 10 ++-- ...-recall-bot-automatic-video-output.util.ts | 3 +- ...is-call-recording-import-complete.util.ts} | 2 +- ...atus-code-to-call-recording-status.util.ts | 2 +- ...ld-complete-call-recording-import.util.ts} | 6 +-- ...s => cleanup-orphaned-recall-bots.test.ts} | 32 ++++++------- ...complete-and-charge-call-recording.test.ts | 12 ++--- .../converge-diverged-call-recordings.test.ts | 22 ++++----- .../__tests__/handle-recall-webhook.test.ts | 24 +++++----- ...ts => import-call-recording-media.test.ts} | 28 +++++------ .../__tests__/reconcile-call-recorder.test.ts | 2 +- ...-bots-for-pending-call-recordings.test.ts} | 24 +++++----- ...s => cleanup-orphaned-recall-bots.util.ts} | 10 ++-- ...complete-and-charge-call-recording.util.ts | 4 +- .../converge-diverged-call-recordings.util.ts | 26 +++++----- .../flows/handle-recall-webhook.util.ts | 28 +++++------ ...ts => import-call-recording-media.util.ts} | 32 ++++++------- ...-call-recording-transcript-result.type.ts} | 2 +- ... import-call-recording-transcript.util.ts} | 10 ++-- .../persist-call-recording-progress.util.ts | 12 ++--- .../flows/reconcile-call-recorder.util.ts | 4 +- ...ule-recall-bot-for-call-recording.util.ts} | 2 +- ...-bots-for-pending-call-recordings.util.ts} | 18 +++---- ... => extract-recall-bot-sync-state.test.ts} | 48 +++++++++---------- ... => extract-recall-bot-sync-state.util.ts} | 6 +-- .../recall-api/recall-bot-api-request.util.ts | 2 +- .../reconcile-stale-bot-state.ts | 48 +++++++++---------- 35 files changed, 247 insertions(+), 250 deletions(-) rename packages/twenty-apps/public/call-recorder/src/logic-functions/data/__tests__/{complete-call-recording-ingestion.test.ts => complete-call-recording-import.test.ts} (80%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/data/{complete-call-recording-ingestion.util.ts => complete-call-recording-import.util.ts} (92%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/{is-call-recording-ingestion-complete.test.ts => is-call-recording-import-complete.test.ts} (82%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/{should-complete-call-recording-ingestion.test.ts => should-complete-call-recording-import.test.ts} (86%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/domain/{is-call-recording-ingestion-complete.util.ts => is-call-recording-import-complete.util.ts} (94%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/domain/{should-complete-call-recording-ingestion.util.ts => should-complete-call-recording-import.util.ts} (85%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/{reap-orphaned-call-recorders.test.ts => cleanup-orphaned-recall-bots.test.ts} (91%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/{ingest-call-recording-media.test.ts => import-call-recording-media.test.ts} (94%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/{heal-call-recordings-missing-bot.test.ts => schedule-recall-bots-for-pending-call-recordings.test.ts} (88%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/{reap-orphaned-call-recorders.util.ts => cleanup-orphaned-recall-bots.util.ts} (93%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/{ingest-call-recording-media.util.ts => import-call-recording-media.util.ts} (87%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/{reconcile-call-recording-transcript-artifact-result.type.ts => import-call-recording-transcript-result.type.ts} (84%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/{reconcile-call-recording-transcript-artifact.util.ts => import-call-recording-transcript.util.ts} (93%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/{ensure-call-recorder.util.ts => schedule-recall-bot-for-call-recording.util.ts} (98%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/flows/{heal-call-recordings-missing-bot.util.ts => schedule-recall-bots-for-pending-call-recordings.util.ts} (78%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/__tests__/{extract-recall-bot-convergence.test.ts => extract-recall-bot-sync-state.test.ts} (74%) rename packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/{extract-recall-bot-convergence.util.ts => extract-recall-bot-sync-state.util.ts} (97%) diff --git a/packages/twenty-apps/public/call-recorder/src/front-components/components/TranscriptEntryListItem.tsx b/packages/twenty-apps/public/call-recorder/src/front-components/components/TranscriptEntryListItem.tsx index 40fd0a304b..50367f3574 100644 --- a/packages/twenty-apps/public/call-recorder/src/front-components/components/TranscriptEntryListItem.tsx +++ b/packages/twenty-apps/public/call-recorder/src/front-components/components/TranscriptEntryListItem.tsx @@ -13,9 +13,7 @@ import { formatSecondsAsClockTimestamp } from 'src/logic-functions/utils/format- const StyledEntry = styled.div<{ $isActive: boolean }>` align-items: flex-start; background: ${({ $isActive }) => - $isActive - ? themeCssVariables.background.transparent.blue - : 'transparent'}; + $isActive ? themeCssVariables.background.transparent.blue : 'transparent'}; border-radius: ${() => themeCssVariables.border.radius.sm}; box-sizing: border-box; display: flex; diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/constants/default-call-recorder-recording-retention-hours.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/constants/default-call-recorder-recording-retention-hours.ts index f1a8cbfdf5..4204e56430 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/constants/default-call-recorder-recording-retention-hours.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/constants/default-call-recorder-recording-retention-hours.ts @@ -1,2 +1,2 @@ -// Twenty stores ingested recording artifacts, so Recall.ai media is temporary. Keep the default below Recall.ai's 168-hour free storage window. +// Twenty stores imported recording artifacts, so Recall.ai media is temporary. Keep the default below Recall.ai's 168-hour free storage window. export const DEFAULT_CALL_RECORDER_RECORDING_RETENTION_HOURS = 166; diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/data/__tests__/complete-call-recording-ingestion.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/data/__tests__/complete-call-recording-import.test.ts similarity index 80% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/data/__tests__/complete-call-recording-ingestion.test.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/data/__tests__/complete-call-recording-import.test.ts index 820f7d3b3a..3cfb493aa8 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/data/__tests__/complete-call-recording-ingestion.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/data/__tests__/complete-call-recording-import.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; -import { completeCallRecordingIngestion } from 'src/logic-functions/data/complete-call-recording-ingestion.util'; +import { completeCallRecordingImport } from 'src/logic-functions/data/complete-call-recording-import.util'; -describe('completeCallRecordingIngestion', () => { +describe('completeCallRecordingImport', () => { it('guards the flip with non-terminal statuses and returns true when the row is claimed', async () => { let capturedArgs: { filter: unknown; data: unknown } | undefined; const mutation = vi.fn(async (mutationArg: any) => { @@ -11,7 +11,7 @@ describe('completeCallRecordingIngestion', () => { return { updateCallRecordings: [{ id: 'call-recording-1' }] }; }); - const claimed = await completeCallRecordingIngestion( + const claimed = await completeCallRecordingImport( { mutation } as never, { id: 'call-recording-1', @@ -30,7 +30,7 @@ describe('completeCallRecordingIngestion', () => { it('returns false when the row was already COMPLETED, so the loser cannot charge', async () => { const mutation = vi.fn(async () => ({ updateCallRecordings: [] })); - const claimed = await completeCallRecordingIngestion( + const claimed = await completeCallRecordingImport( { mutation } as never, { id: 'call-recording-1', @@ -43,7 +43,7 @@ describe('completeCallRecordingIngestion', () => { it('returns false when the API omits the result list', async () => { const mutation = vi.fn(async () => ({})); - const claimed = await completeCallRecordingIngestion( + const claimed = await completeCallRecordingImport( { mutation } as never, { id: 'call-recording-1', diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/data/complete-call-recording-ingestion.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/data/complete-call-recording-import.util.ts similarity index 92% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/data/complete-call-recording-ingestion.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/data/complete-call-recording-import.util.ts index 659c601af9..a844806331 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/data/complete-call-recording-ingestion.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/data/complete-call-recording-import.util.ts @@ -3,7 +3,7 @@ import { type CoreApiClient } from 'twenty-client-sdk/core'; import { CallRecordingStatus } from 'src/logic-functions/constants/call-recording-status'; import { NON_TERMINAL_CALL_RECORDING_STATUSES } from 'src/logic-functions/constants/non-terminal-call-recording-statuses'; -export const completeCallRecordingIngestion = async ( +export const completeCallRecordingImport = async ( client: CoreApiClient, { id }: { id: string }, ): Promise => { diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/data/request-call-recording-summaries-backfill.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/data/request-call-recording-summaries-backfill.util.ts index 27b5f42faf..9037f8652b 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/data/request-call-recording-summaries-backfill.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/data/request-call-recording-summaries-backfill.util.ts @@ -1,8 +1,9 @@ import { GENERATE_CALL_RECORDING_SUMMARIES_ROUTE_PATH } from 'src/constants/generate-call-recording-summaries-route-path'; import { postToOwnRoute } from 'src/logic-functions/data/post-to-own-route.util'; -export const requestCallRecordingSummariesBackfill = async (): Promise => - postToOwnRoute({ - path: GENERATE_CALL_RECORDING_SUMMARIES_ROUTE_PATH, - body: {}, - }); +export const requestCallRecordingSummariesBackfill = + async (): Promise => + postToOwnRoute({ + path: GENERATE_CALL_RECORDING_SUMMARIES_ROUTE_PATH, + body: {}, + }); diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/extract-conference-link-url-from-text.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/extract-conference-link-url-from-text.test.ts index 4307e1dfad..bbf87db52a 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/extract-conference-link-url-from-text.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/extract-conference-link-url-from-text.test.ts @@ -64,7 +64,10 @@ describe('extractConferenceLinkUrlFromText', () => { }); it.each([ - ['a non-conferencing URL', 'Agenda: https://docs.google.com/document/d/abc'], + [ + 'a non-conferencing URL', + 'Agenda: https://docs.google.com/document/d/abc', + ], ['a Zoom marketing page URL', 'Learn more at https://zoom.us/pricing'], ['plain text without links', 'Conference Room A'], ['an empty string', ''], diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/is-call-recording-ingestion-complete.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/is-call-recording-import-complete.test.ts similarity index 82% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/is-call-recording-ingestion-complete.test.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/is-call-recording-import-complete.test.ts index 11e00d23ca..5a991693d2 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/is-call-recording-ingestion-complete.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/is-call-recording-import-complete.test.ts @@ -1,15 +1,15 @@ import { describe, expect, it } from 'vitest'; -import { isCallRecordingIngestionComplete } from 'src/logic-functions/domain/is-call-recording-ingestion-complete.util'; +import { isCallRecordingImportComplete } from 'src/logic-functions/domain/is-call-recording-import-complete.util'; const AUDIO_VALUE = [{ fileId: 'file-audio-1', label: 'audio.mp3' }]; const VIDEO_VALUE = [{ fileId: 'file-video-1', label: 'video.mp4' }]; const TRANSCRIPT_CONTENT = [{ participant: { id: 1 }, words: [] }]; -describe('isCallRecordingIngestionComplete', () => { +describe('isCallRecordingImportComplete', () => { it('is complete when transcript content and both media files are present', () => { expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: TRANSCRIPT_CONTENT, audio: AUDIO_VALUE, video: VIDEO_VALUE, @@ -20,7 +20,7 @@ describe('isCallRecordingIngestionComplete', () => { it('is incomplete while the transcript holds a marker', () => { expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: { recallTranscriptId: 'recall-transcript-1', status: 'PENDING', @@ -34,7 +34,7 @@ describe('isCallRecordingIngestionComplete', () => { it('is incomplete when the transcript is unset', () => { expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: null, audio: AUDIO_VALUE, video: VIDEO_VALUE, @@ -45,7 +45,7 @@ describe('isCallRecordingIngestionComplete', () => { it('is incomplete while any media field is empty', () => { expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: TRANSCRIPT_CONTENT, audio: undefined, video: VIDEO_VALUE, @@ -53,7 +53,7 @@ describe('isCallRecordingIngestionComplete', () => { }), ).toBe(false); expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: TRANSCRIPT_CONTENT, audio: AUDIO_VALUE, video: [], @@ -64,7 +64,7 @@ describe('isCallRecordingIngestionComplete', () => { it('treats a media file skipped for size as resolved', () => { expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: TRANSCRIPT_CONTENT, audio: AUDIO_VALUE, video: undefined, @@ -72,7 +72,7 @@ describe('isCallRecordingIngestionComplete', () => { }), ).toBe(true); expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: TRANSCRIPT_CONTENT, audio: undefined, video: VIDEO_VALUE, @@ -80,7 +80,7 @@ describe('isCallRecordingIngestionComplete', () => { }), ).toBe(true); expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: TRANSCRIPT_CONTENT, audio: undefined, video: undefined, @@ -91,7 +91,7 @@ describe('isCallRecordingIngestionComplete', () => { it('does not let a size marker excuse the other missing artifact', () => { expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: TRANSCRIPT_CONTENT, audio: undefined, video: VIDEO_VALUE, @@ -102,7 +102,7 @@ describe('isCallRecordingIngestionComplete', () => { it('ignores unrelated failure reasons', () => { expect( - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: TRANSCRIPT_CONTENT, audio: AUDIO_VALUE, video: undefined, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/should-complete-call-recording-ingestion.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/should-complete-call-recording-import.test.ts similarity index 86% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/should-complete-call-recording-ingestion.test.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/should-complete-call-recording-import.test.ts index bae388da11..d95db419dc 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/should-complete-call-recording-ingestion.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/__tests__/should-complete-call-recording-import.test.ts @@ -1,16 +1,16 @@ import { describe, expect, it } from 'vitest'; import { CallRecordingStatus } from 'src/logic-functions/constants/call-recording-status'; -import { shouldCompleteCallRecordingIngestion } from 'src/logic-functions/domain/should-complete-call-recording-ingestion.util'; +import { shouldCompleteCallRecordingImport } from 'src/logic-functions/domain/should-complete-call-recording-import.util'; const filledTranscript = [{ participant: { id: 1 }, words: [] }]; const filledAudio = [{ fileId: 'file-audio-1', label: 'audio.mp3' }]; const filledVideo = [{ fileId: 'file-video-1', label: 'video.mp4' }]; -describe('shouldCompleteCallRecordingIngestion', () => { +describe('shouldCompleteCallRecordingImport', () => { it('requires complete artifacts and billable timestamps before completion', () => { expect( - shouldCompleteCallRecordingIngestion({ + shouldCompleteCallRecordingImport({ current: { status: CallRecordingStatus.PROCESSING, startedAt: '2026-06-10T09:00:00.000Z', @@ -24,7 +24,7 @@ describe('shouldCompleteCallRecordingIngestion', () => { ).toBe(true); expect( - shouldCompleteCallRecordingIngestion({ + shouldCompleteCallRecordingImport({ current: { status: CallRecordingStatus.PROCESSING, endedAt: '2026-06-10T10:00:00.000Z', @@ -37,7 +37,7 @@ describe('shouldCompleteCallRecordingIngestion', () => { ).toBe(false); expect( - shouldCompleteCallRecordingIngestion({ + shouldCompleteCallRecordingImport({ current: { status: CallRecordingStatus.PROCESSING, transcript: filledTranscript, @@ -52,7 +52,7 @@ describe('shouldCompleteCallRecordingIngestion', () => { ).toBe(true); expect( - shouldCompleteCallRecordingIngestion({ + shouldCompleteCallRecordingImport({ current: { status: CallRecordingStatus.PROCESSING, startedAt: '2026-06-10T10:00:00.000Z', @@ -68,7 +68,7 @@ describe('shouldCompleteCallRecordingIngestion', () => { it('completes when a missing media file is marked too large', () => { expect( - shouldCompleteCallRecordingIngestion({ + shouldCompleteCallRecordingImport({ current: { status: CallRecordingStatus.PROCESSING, startedAt: '2026-06-10T09:00:00.000Z', @@ -83,7 +83,7 @@ describe('shouldCompleteCallRecordingIngestion', () => { ).toBe(true); expect( - shouldCompleteCallRecordingIngestion({ + shouldCompleteCallRecordingImport({ current: { status: CallRecordingStatus.PROCESSING, startedAt: '2026-06-10T09:00:00.000Z', @@ -99,7 +99,7 @@ describe('shouldCompleteCallRecordingIngestion', () => { it('does not complete a persisted failed recording', () => { expect( - shouldCompleteCallRecordingIngestion({ + shouldCompleteCallRecordingImport({ current: { status: CallRecordingStatus.FAILED, startedAt: '2026-06-10T09:00:00.000Z', @@ -115,7 +115,7 @@ describe('shouldCompleteCallRecordingIngestion', () => { it('does not complete when the incoming update marks the recording as failed', () => { expect( - shouldCompleteCallRecordingIngestion({ + shouldCompleteCallRecordingImport({ current: { status: CallRecordingStatus.PROCESSING, startedAt: '2026-06-10T09:00:00.000Z', diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/build-bot-image.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/build-bot-image.util.ts index 396e2f3a2d..0362de4d84 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/build-bot-image.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/build-bot-image.util.ts @@ -107,13 +107,9 @@ const composeJpeg = ({ .jpeg({ quality, mozjpeg: true }) .toBuffer(); -const buildRecordingStatusBadge = ( - sharp: SharpFactory, -): Promise => { - const badgeCornerRadius = - RECALL_BOT_IMAGE_RECORDING_STATUS_BADGE_HEIGHT / 2; - const recordingDotCenter = - RECALL_BOT_IMAGE_RECORDING_STATUS_BADGE_HEIGHT / 2; +const buildRecordingStatusBadge = (sharp: SharpFactory): Promise => { + const badgeCornerRadius = RECALL_BOT_IMAGE_RECORDING_STATUS_BADGE_HEIGHT / 2; + const recordingDotCenter = RECALL_BOT_IMAGE_RECORDING_STATUS_BADGE_HEIGHT / 2; const recordingDotRadius = RECALL_BOT_IMAGE_RECORDING_STATUS_BADGE_DOT_DIAMETER / 2; const recordingStatusTextBaseline = diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/build-recall-bot-automatic-video-output.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/build-recall-bot-automatic-video-output.util.ts index 3826e1a636..59b536f30c 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/build-recall-bot-automatic-video-output.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/build-recall-bot-automatic-video-output.util.ts @@ -32,8 +32,7 @@ export const buildRecallBotAutomaticVideoOutput = async (): Promise< logoBuffer, background, withRecordingStatusBadge: true, - })) ?? - notRecordingImage; + })) ?? notRecordingImage; return { in_call_recording: { kind: 'jpeg', b64_data: recordingImage }, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/is-call-recording-ingestion-complete.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/is-call-recording-import-complete.util.ts similarity index 94% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/domain/is-call-recording-ingestion-complete.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/domain/is-call-recording-import-complete.util.ts index 0bc970bcb7..6bc0d8ddb1 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/is-call-recording-ingestion-complete.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/is-call-recording-import-complete.util.ts @@ -4,7 +4,7 @@ import { type FilesFieldValue } from 'src/logic-functions/types/files-field-valu import { parseMediaFileTooLargeMarkers } from 'src/logic-functions/domain/parse-media-file-too-large-markers.util'; import { parseTranscriptMarker } from 'src/logic-functions/domain/parse-transcript-marker.util'; -export const isCallRecordingIngestionComplete = ({ +export const isCallRecordingImportComplete = ({ transcript, audio, video, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/map-recall-status-code-to-call-recording-status.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/map-recall-status-code-to-call-recording-status.util.ts index 9af31afc32..aee5e3b413 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/map-recall-status-code-to-call-recording-status.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/map-recall-status-code-to-call-recording-status.util.ts @@ -11,7 +11,7 @@ export const mapRecallStatusCodeToCallRecordingStatus = ( case 'recording_permission_allowed': case 'in_call_recording': return CallRecordingStatus.RECORDING; - // 'done' stays PROCESSING: COMPLETED is set only after all artifacts are ingested. + // 'done' stays PROCESSING: COMPLETED is set only after all artifacts are imported. case 'call_ended': case 'analysis_done': case 'done': diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/should-complete-call-recording-ingestion.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/should-complete-call-recording-import.util.ts similarity index 85% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/domain/should-complete-call-recording-ingestion.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/domain/should-complete-call-recording-import.util.ts index cf1c711401..c6a25f94e3 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/should-complete-call-recording-ingestion.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/domain/should-complete-call-recording-import.util.ts @@ -1,10 +1,10 @@ import { CallRecordingStatus } from 'src/logic-functions/constants/call-recording-status'; import { type FilesFieldValue } from 'src/logic-functions/types/files-field-value.type'; import { computeCallRecordingCharge } from 'src/logic-functions/domain/compute-call-recording-charge.util'; -import { isCallRecordingIngestionComplete } from 'src/logic-functions/domain/is-call-recording-ingestion-complete.util'; +import { isCallRecordingImportComplete } from 'src/logic-functions/domain/is-call-recording-import-complete.util'; import { type CallRecordingUpdateFields } from 'src/logic-functions/types/call-recording-update-fields.type'; -export const shouldCompleteCallRecordingIngestion = ({ +export const shouldCompleteCallRecordingImport = ({ current, updateData, }: { @@ -26,7 +26,7 @@ export const shouldCompleteCallRecordingIngestion = ({ startedAt: updateData.startedAt ?? current.startedAt, endedAt: updateData.endedAt ?? current.endedAt, }) !== undefined && - isCallRecordingIngestionComplete({ + isCallRecordingImportComplete({ transcript: updateData.transcript ?? current.transcript, audio: updateData.audio ?? current.audio, video: updateData.video ?? current.video, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/reap-orphaned-call-recorders.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/cleanup-orphaned-recall-bots.test.ts similarity index 91% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/reap-orphaned-call-recorders.test.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/cleanup-orphaned-recall-bots.test.ts index c46d7f47c3..33270ab0b9 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/reap-orphaned-call-recorders.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/cleanup-orphaned-recall-bots.test.ts @@ -1,7 +1,7 @@ import { type CoreApiClient } from 'twenty-client-sdk/core'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { reapOrphanedCallRecorders } from 'src/logic-functions/flows/reap-orphaned-call-recorders.util'; +import { cleanupOrphanedRecallBots } from 'src/logic-functions/flows/cleanup-orphaned-recall-bots.util'; const listScheduledRecallBotsMock = vi.hoisted(() => vi.fn()); const cancelRecallBotMock = vi.hoisted(() => vi.fn()); @@ -89,7 +89,7 @@ const buildCurrentWorkspaceBot = ({ twentyWorkspaceId: CURRENT_WORKSPACE_ID, }); -describe('reapOrphanedCallRecorders', () => { +describe('cleanupOrphanedRecallBots', () => { beforeEach(() => { vi.spyOn(console, 'warn').mockImplementation(() => {}); getCurrentWorkspaceIdMock.mockReset(); @@ -112,7 +112,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([ { id: 'call-recording-1', @@ -142,7 +142,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([ { id: 'call-recording-1', @@ -178,7 +178,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([ { id: 'call-recording-1', @@ -211,7 +211,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([]), joinAtAfter: JOIN_AT_AFTER, joinAtBefore: JOIN_AT_BEFORE, @@ -234,7 +234,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([ { id: 'call-recording-1', @@ -259,7 +259,7 @@ describe('reapOrphanedCallRecorders', () => { bots: [buildBot({ id: 'unrelated-bot' })], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([]), joinAtAfter: JOIN_AT_AFTER, joinAtBefore: JOIN_AT_BEFORE, @@ -283,7 +283,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([]), joinAtAfter: JOIN_AT_AFTER, joinAtBefore: JOIN_AT_BEFORE, @@ -308,7 +308,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([]), joinAtAfter: JOIN_AT_AFTER, joinAtBefore: JOIN_AT_BEFORE, @@ -332,7 +332,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([]), joinAtAfter: JOIN_AT_AFTER, joinAtBefore: JOIN_AT_BEFORE, @@ -363,7 +363,7 @@ describe('reapOrphanedCallRecorders', () => { errorMessage: 'Recall API responded with HTTP 409', }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([]), joinAtAfter: JOIN_AT_AFTER, joinAtBefore: JOIN_AT_BEFORE, @@ -378,14 +378,14 @@ describe('reapOrphanedCallRecorders', () => { }); }); - it('reports nothing reaped when listing Recall bots fails', async () => { + it('reports nothing canceled when listing Recall bots fails', async () => { listScheduledRecallBotsMock.mockResolvedValue({ ok: false, status: 500, errorMessage: 'Recall API responded with HTTP 500', }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([]), joinAtAfter: JOIN_AT_AFTER, joinAtBefore: JOIN_AT_BEFORE, @@ -398,7 +398,7 @@ describe('reapOrphanedCallRecorders', () => { expect(cancelRecallBotMock).not.toHaveBeenCalled(); }); - it('skips reaping when the current workspace cannot be resolved', async () => { + it('skips cancellation when the current workspace cannot be resolved', async () => { getCurrentWorkspaceIdMock.mockReturnValue(undefined); listScheduledRecallBotsMock.mockResolvedValue({ ok: true, @@ -410,7 +410,7 @@ describe('reapOrphanedCallRecorders', () => { ], }); - const result = await reapOrphanedCallRecorders({ + const result = await cleanupOrphanedRecallBots({ client: buildClient([]), joinAtAfter: JOIN_AT_AFTER, joinAtBefore: JOIN_AT_BEFORE, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/complete-and-charge-call-recording.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/complete-and-charge-call-recording.test.ts index 42c48e63cf..b44f4604c9 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/complete-and-charge-call-recording.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/complete-and-charge-call-recording.test.ts @@ -1,12 +1,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -const completeCallRecordingIngestionMock = vi.hoisted(() => vi.fn()); +const completeCallRecordingImportMock = vi.hoisted(() => vi.fn()); const chargeCompletedCallRecordingMock = vi.hoisted(() => vi.fn()); vi.mock( - 'src/logic-functions/data/complete-call-recording-ingestion.util', + 'src/logic-functions/data/complete-call-recording-import.util', () => ({ - completeCallRecordingIngestion: completeCallRecordingIngestionMock, + completeCallRecordingImport: completeCallRecordingImportMock, }), ); @@ -25,7 +25,7 @@ describe('completeAndChargeCallRecording', () => { }); it('charges exactly once when this path wins the completion claim', async () => { - completeCallRecordingIngestionMock.mockResolvedValue(true); + completeCallRecordingImportMock.mockResolvedValue(true); const claimed = await completeAndChargeCallRecording({} as never, { id: 'call-recording-1', @@ -34,7 +34,7 @@ describe('completeAndChargeCallRecording', () => { }); expect(claimed).toBe(true); - expect(completeCallRecordingIngestionMock).toHaveBeenCalledWith( + expect(completeCallRecordingImportMock).toHaveBeenCalledWith( {}, { id: 'call-recording-1' }, ); @@ -47,7 +47,7 @@ describe('completeAndChargeCallRecording', () => { }); it('does not charge when another path already completed the recording', async () => { - completeCallRecordingIngestionMock.mockResolvedValue(false); + completeCallRecordingImportMock.mockResolvedValue(false); const claimed = await completeAndChargeCallRecording({} as never, { id: 'call-recording-1', diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/converge-diverged-call-recordings.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/converge-diverged-call-recordings.test.ts index 660e969574..3747012adf 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/converge-diverged-call-recordings.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/converge-diverged-call-recordings.test.ts @@ -7,7 +7,7 @@ const getRecallBotMock = vi.hoisted(() => vi.fn()); const listRecallTranscriptsMock = vi.hoisted(() => vi.fn()); const createAsyncRecallTranscriptMock = vi.hoisted(() => vi.fn()); const downloadTranscriptMock = vi.hoisted(() => vi.fn()); -const ingestCallRecordingMediaMock = vi.hoisted(() => vi.fn()); +const importCallRecordingMediaMock = vi.hoisted(() => vi.fn()); const chargeCompletedCallRecordingMock = vi.hoisted(() => vi.fn()); vi.mock('src/logic-functions/recall-api/get-recall-bot.util', () => ({ @@ -29,8 +29,8 @@ vi.mock('src/logic-functions/flows/download-transcript.util', () => ({ downloadTranscript: downloadTranscriptMock, })); -vi.mock('src/logic-functions/flows/ingest-call-recording-media.util', () => ({ - ingestCallRecordingMedia: ingestCallRecordingMediaMock, +vi.mock('src/logic-functions/flows/import-call-recording-media.util', () => ({ + importCallRecordingMedia: importCallRecordingMediaMock, })); vi.mock( @@ -115,8 +115,8 @@ describe('convergeDivergedCallRecordings', () => { }); downloadTranscriptMock.mockReset(); downloadTranscriptMock.mockResolvedValue({ outcome: 'pending' }); - ingestCallRecordingMediaMock.mockReset(); - ingestCallRecordingMediaMock.mockResolvedValue({}); + importCallRecordingMediaMock.mockReset(); + importCallRecordingMediaMock.mockResolvedValue({}); chargeCompletedCallRecordingMock.mockReset(); chargeCompletedCallRecordingMock.mockResolvedValue(undefined); }); @@ -149,7 +149,7 @@ describe('convergeDivergedCallRecordings', () => { expect(getRecallBotMock).toHaveBeenCalledWith({ externalBotId: 'recall-bot-1', }); - expect(ingestCallRecordingMediaMock).toHaveBeenCalledWith({ + expect(importCallRecordingMediaMock).toHaveBeenCalledWith({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -201,7 +201,7 @@ describe('convergeDivergedCallRecordings', () => { }); expect(listRecallTranscriptsMock).not.toHaveBeenCalled(); - expect(ingestCallRecordingMediaMock).not.toHaveBeenCalled(); + expect(importCallRecordingMediaMock).not.toHaveBeenCalled(); expect(client.mutations).toEqual([ { id: 'call-recording-1', @@ -230,7 +230,7 @@ describe('convergeDivergedCallRecordings', () => { ], }, }); - ingestCallRecordingMediaMock.mockResolvedValue({ + importCallRecordingMediaMock.mockResolvedValue({ audio: [{ fileId: 'file-audio-1', label: 'audio.mp3' }], video: [{ fileId: 'file-video-1', label: 'video.mp4' }], }); @@ -295,7 +295,7 @@ describe('convergeDivergedCallRecordings', () => { ], }, }); - ingestCallRecordingMediaMock.mockResolvedValue({ + importCallRecordingMediaMock.mockResolvedValue({ audio: [{ fileId: 'file-audio-1', label: 'audio.mp3' }], callRecorderFailureReason: 'video_file_too_large', }); @@ -368,7 +368,7 @@ describe('convergeDivergedCallRecordings', () => { now: NOW, }); - expect(ingestCallRecordingMediaMock).toHaveBeenCalledWith({ + expect(importCallRecordingMediaMock).toHaveBeenCalledWith({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: true, @@ -404,7 +404,7 @@ describe('convergeDivergedCallRecordings', () => { ], }, }); - ingestCallRecordingMediaMock.mockResolvedValue({ + importCallRecordingMediaMock.mockResolvedValue({ audio: [{ fileId: 'file-audio-1', label: 'audio.mp3' }], callRecorderFailureReason: 'video_file_too_large', }); diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts index d21742945a..d02acc7c13 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts @@ -25,7 +25,7 @@ const getRecallBotMock = vi.hoisted(() => vi.fn()); const listRecallTranscriptsMock = vi.hoisted(() => vi.fn()); const createAsyncRecallTranscriptMock = vi.hoisted(() => vi.fn()); const retrieveRecallTranscriptMock = vi.hoisted(() => vi.fn()); -const ingestCallRecordingMediaMock = vi.hoisted(() => vi.fn()); +const importCallRecordingMediaMock = vi.hoisted(() => vi.fn()); const chargeCompletedCallRecordingMock = vi.hoisted(() => vi.fn()); vi.mock('src/logic-functions/recall-api/get-recall-bot.util', () => ({ @@ -50,8 +50,8 @@ vi.mock( }), ); -vi.mock('src/logic-functions/flows/ingest-call-recording-media.util', () => ({ - ingestCallRecordingMedia: ingestCallRecordingMediaMock, +vi.mock('src/logic-functions/flows/import-call-recording-media.util', () => ({ + importCallRecordingMedia: importCallRecordingMediaMock, })); vi.mock( @@ -168,8 +168,8 @@ describe('handleRecallWebhook', () => { status: null, errorMessage: 'transcript retrieval disabled in test', }); - ingestCallRecordingMediaMock.mockReset(); - ingestCallRecordingMediaMock.mockResolvedValue({}); + importCallRecordingMediaMock.mockReset(); + importCallRecordingMediaMock.mockResolvedValue({}); chargeCompletedCallRecordingMock.mockReset(); chargeCompletedCallRecordingMock.mockResolvedValue(undefined); }); @@ -882,12 +882,12 @@ describe('handleRecallWebhook', () => { ]); }); - it('ingests media on recording.done and completes once all artifacts are present', async () => { + it('imports media on recording.done and completes once all artifacts are present', async () => { getRecallBotMock.mockResolvedValue({ ok: true, bot: { id: 'recall-bot-1' }, }); - ingestCallRecordingMediaMock.mockResolvedValue({ + importCallRecordingMediaMock.mockResolvedValue({ audio: [{ fileId: 'file-audio-1', label: 'audio.mp3' }], video: [{ fileId: 'file-video-1', label: 'video.mp4' }], }); @@ -908,7 +908,7 @@ describe('handleRecallWebhook', () => { body: buildRecordingDoneWebhookBody(), }); - expect(ingestCallRecordingMediaMock).toHaveBeenCalledWith({ + expect(importCallRecordingMediaMock).toHaveBeenCalledWith({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -941,7 +941,7 @@ describe('handleRecallWebhook', () => { ok: true, bot: { id: 'recall-bot-1' }, }); - ingestCallRecordingMediaMock.mockResolvedValue({ + importCallRecordingMediaMock.mockResolvedValue({ audio: [{ fileId: 'file-audio-1', label: 'audio.mp3' }], callRecorderFailureReason: 'video_file_too_large', }); @@ -995,7 +995,7 @@ describe('handleRecallWebhook', () => { ok: true, bot: { id: 'recall-bot-1' }, }); - ingestCallRecordingMediaMock.mockResolvedValue({ + importCallRecordingMediaMock.mockResolvedValue({ audio: [{ fileId: 'file-audio-1', label: 'audio.mp3' }], callRecorderFailureReason: 'video_file_too_large', }); @@ -1045,7 +1045,7 @@ describe('handleRecallWebhook', () => { ok: true, bot: { id: 'recall-bot-1' }, }); - ingestCallRecordingMediaMock.mockResolvedValue({ + importCallRecordingMediaMock.mockResolvedValue({ audio: [{ fileId: 'file-audio-1', label: 'audio.mp3' }], }); createAsyncRecallTranscriptMock.mockResolvedValue({ @@ -1135,7 +1135,7 @@ describe('handleRecallWebhook', () => { expect(chargeCompletedCallRecordingMock).not.toHaveBeenCalled(); }); - it('completes and charges on transcript.done when media is already ingested', async () => { + it('completes and charges on transcript.done when media is already imported', async () => { const transcriptContent = [ { participant: { id: 1, name: 'Alice' }, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/ingest-call-recording-media.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/import-call-recording-media.test.ts similarity index 94% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/ingest-call-recording-media.test.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/import-call-recording-media.test.ts index 08a8372fe3..80824fda56 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/ingest-call-recording-media.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/import-call-recording-media.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { CALL_RECORDING_VIDEO_FIELD_UNIVERSAL_IDENTIFIER } from 'src/constants/call-recording-video-field-universal-identifier'; import { CALL_RECORDER_MAX_MEDIA_FILE_SIZE_MB_ENV_VAR_NAME } from 'src/logic-functions/constants/call-recorder-max-media-file-size-mb-env-var-name'; -import { ingestCallRecordingMedia } from 'src/logic-functions/flows/ingest-call-recording-media.util'; +import { importCallRecordingMedia } from 'src/logic-functions/flows/import-call-recording-media.util'; const mutationMock = vi.hoisted(() => vi.fn()); const getRecallRecordingMock = vi.hoisted(() => vi.fn()); @@ -147,7 +147,7 @@ const getUploadBridgeCall = (fileName: string) => ([uploadInput]) => uploadInput.fileName === fileName, ); -describe('ingestCallRecordingMedia', () => { +describe('importCallRecordingMedia', () => { beforeEach(() => { vi.spyOn(console, 'warn').mockImplementation(() => {}); vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -179,7 +179,7 @@ describe('ingestCallRecordingMedia', () => { }); it('streams and uploads every missing artifact', async () => { - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -211,7 +211,7 @@ describe('ingestCallRecordingMedia', () => { }); it('declares the presigned upload with the download size, folder and field identifier', async () => { - await ingestCallRecordingMedia({ + await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: true, @@ -241,7 +241,7 @@ describe('ingestCallRecordingMedia', () => { }); it('skips artifacts already on the record', async () => { - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -255,7 +255,7 @@ describe('ingestCallRecordingMedia', () => { }); it('does not fetch the recording when both artifacts are present', async () => { - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: true, @@ -289,7 +289,7 @@ describe('ingestCallRecordingMedia', () => { }, }); - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -318,7 +318,7 @@ describe('ingestCallRecordingMedia', () => { }, }); - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -343,7 +343,7 @@ describe('ingestCallRecordingMedia', () => { recording: { id: 'recall-recording-1' }, }); - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -361,7 +361,7 @@ describe('ingestCallRecordingMedia', () => { errorMessage: 'recording boom', }); - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -388,7 +388,7 @@ describe('ingestCallRecordingMedia', () => { }, }); - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -421,7 +421,7 @@ describe('ingestCallRecordingMedia', () => { }, }); - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -445,7 +445,7 @@ describe('ingestCallRecordingMedia', () => { }, }); - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: false, @@ -471,7 +471,7 @@ describe('ingestCallRecordingMedia', () => { }, }); - const updateFields = await ingestCallRecordingMedia({ + const updateFields = await importCallRecordingMedia({ callRecordingId: 'call-recording-1', externalRecordingId: 'recall-recording-1', hasAudio: true, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/reconcile-call-recorder.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/reconcile-call-recorder.test.ts index fb6e7f65e5..e5524b6734 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/reconcile-call-recorder.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/reconcile-call-recorder.test.ts @@ -929,7 +929,7 @@ describe('reconcileCallRecorderForCalendarEventIds', () => { expect(rescheduleRecallBotMock).toHaveBeenCalledWith( expect.objectContaining({ externalBotId: 'recall-bot-stale' }), ); - // The event path no longer re-creates the bot; the stale id is cleared and the cron heals the botless row. + // The event path no longer re-creates the bot; the stale id is cleared and the cron schedules a bot for the pending row. expect(scheduleRecallBotMock).not.toHaveBeenCalled(); expect(client.callRecordings).toEqual([ expect.objectContaining({ diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/heal-call-recordings-missing-bot.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/schedule-recall-bots-for-pending-call-recordings.test.ts similarity index 88% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/heal-call-recordings-missing-bot.test.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/schedule-recall-bots-for-pending-call-recordings.test.ts index 4d732bd9c1..edbd5332e3 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/heal-call-recordings-missing-bot.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/schedule-recall-bots-for-pending-call-recordings.test.ts @@ -1,7 +1,7 @@ import { type CoreApiClient } from 'twenty-client-sdk/core'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { healCallRecordingsMissingBot } from 'src/logic-functions/flows/heal-call-recordings-missing-bot.util'; +import { scheduleRecallBotsForPendingCallRecordings } from 'src/logic-functions/flows/schedule-recall-bots-for-pending-call-recordings.util'; const scheduleRecallBotMock = vi.hoisted(() => vi.fn()); const getCurrentWorkspaceIdMock = vi.hoisted(() => vi.fn()); @@ -108,7 +108,7 @@ const buildConnection = (nodes: Node[]) => ({ edges: nodes.map((node) => ({ node })), }); -const buildBotlessCallRecording = ( +const buildPendingCallRecording = ( overrides: Partial = {}, ): CallRecordingNode => ({ id: 'call-recording-1', @@ -130,7 +130,7 @@ const buildCalendarEvent = ( ...overrides, }); -describe('healCallRecordingsMissingBot', () => { +describe('scheduleRecallBotsForPendingCallRecordings', () => { beforeEach(() => { vi.spyOn(console, 'warn').mockImplementation(() => {}); getCurrentWorkspaceIdMock.mockReset(); @@ -142,13 +142,13 @@ describe('healCallRecordingsMissingBot', () => { }); }); - it('schedules a bot and writes the id for an upcoming botless recording', async () => { + it('schedules a bot and writes the id for an upcoming pending recording', async () => { const client = new FakeCoreApiClient({ - callRecordings: [buildBotlessCallRecording()], + callRecordings: [buildPendingCallRecording()], calendarEvents: [buildCalendarEvent()], }); - const result = await healCallRecordingsMissingBot({ + const result = await scheduleRecallBotsForPendingCallRecordings({ client: client as unknown as CoreApiClient, now: NOW, }); @@ -172,11 +172,11 @@ describe('healCallRecordingsMissingBot', () => { errorMessage: 'Recall API responded with HTTP 500', }); const client = new FakeCoreApiClient({ - callRecordings: [buildBotlessCallRecording()], + callRecordings: [buildPendingCallRecording()], calendarEvents: [buildCalendarEvent()], }); - const result = await healCallRecordingsMissingBot({ + const result = await scheduleRecallBotsForPendingCallRecordings({ client: client as unknown as CoreApiClient, now: NOW, }); @@ -188,7 +188,7 @@ describe('healCallRecordingsMissingBot', () => { it('skips a recording whose meeting has already ended', async () => { const client = new FakeCoreApiClient({ - callRecordings: [buildBotlessCallRecording()], + callRecordings: [buildPendingCallRecording()], calendarEvents: [ buildCalendarEvent({ startsAt: PAST_STARTS_AT, @@ -197,7 +197,7 @@ describe('healCallRecordingsMissingBot', () => { ], }); - const result = await healCallRecordingsMissingBot({ + const result = await scheduleRecallBotsForPendingCallRecordings({ client: client as unknown as CoreApiClient, now: NOW, }); @@ -209,12 +209,12 @@ describe('healCallRecordingsMissingBot', () => { it('does nothing when every scheduled recording already has a bot', async () => { const client = new FakeCoreApiClient({ callRecordings: [ - buildBotlessCallRecording({ externalBotId: 'recall-bot-existing' }), + buildPendingCallRecording({ externalBotId: 'recall-bot-existing' }), ], calendarEvents: [buildCalendarEvent()], }); - const result = await healCallRecordingsMissingBot({ + const result = await scheduleRecallBotsForPendingCallRecordings({ client: client as unknown as CoreApiClient, now: NOW, }); diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reap-orphaned-call-recorders.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/cleanup-orphaned-recall-bots.util.ts similarity index 93% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reap-orphaned-call-recorders.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/cleanup-orphaned-recall-bots.util.ts index 42445a3aa7..57448b498f 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reap-orphaned-call-recorders.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/cleanup-orphaned-recall-bots.util.ts @@ -14,13 +14,13 @@ import { type RecallScheduledBot, } from 'src/logic-functions/recall-api/list-scheduled-recall-bots.util'; -export type ReapOrphanedCallRecordersResult = { +export type CleanupOrphanedRecallBotsResult = { scannedBotCount: number; canceledExternalBotIds: string[]; }; // Bots no open CallRecording request claims would still join; cancel them on Recall. -export const reapOrphanedCallRecorders = async ({ +export const cleanupOrphanedRecallBots = async ({ client, joinAtAfter, joinAtBefore, @@ -28,7 +28,7 @@ export const reapOrphanedCallRecorders = async ({ client: CoreApiClient; joinAtAfter: string; joinAtBefore: string; -}): Promise => { +}): Promise => { const listResult = await listScheduledRecallBots({ joinAtAfter, joinAtBefore, @@ -36,7 +36,7 @@ export const reapOrphanedCallRecorders = async ({ if (!listResult.ok) { console.warn( - `[call-recorder] failed to list Recall bots for orphan reaping: ${listResult.errorMessage}`, + `[call-recorder] failed to list Recall bots for orphan cancellation: ${listResult.errorMessage}`, ); return { scannedBotCount: 0, canceledExternalBotIds: [] }; @@ -46,7 +46,7 @@ export const reapOrphanedCallRecorders = async ({ if (isUndefined(currentWorkspaceId)) { console.warn( - '[call-recorder] cannot reap orphaned Recall bots: workspace id unavailable', + '[call-recorder] cannot cancel orphaned Recall bots: workspace id unavailable', ); return { diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/complete-and-charge-call-recording.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/complete-and-charge-call-recording.util.ts index 1c1dff964b..a0ea7a7210 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/complete-and-charge-call-recording.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/complete-and-charge-call-recording.util.ts @@ -1,6 +1,6 @@ import { type CoreApiClient } from 'twenty-client-sdk/core'; -import { completeCallRecordingIngestion } from 'src/logic-functions/data/complete-call-recording-ingestion.util'; +import { completeCallRecordingImport } from 'src/logic-functions/data/complete-call-recording-import.util'; import { chargeCompletedCallRecording } from 'src/logic-functions/flows/charge-completed-call-recording.util'; export const completeAndChargeCallRecording = async ( @@ -15,7 +15,7 @@ export const completeAndChargeCallRecording = async ( endedAt: string | undefined; }, ): Promise => { - const claimed = await completeCallRecordingIngestion(client, { id }); + const claimed = await completeCallRecordingImport(client, { id }); if (claimed) { await chargeCompletedCallRecording({ diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/converge-diverged-call-recordings.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/converge-diverged-call-recordings.util.ts index 019fe609a7..37856aed26 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/converge-diverged-call-recordings.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/converge-diverged-call-recordings.util.ts @@ -7,22 +7,22 @@ import { NON_TERMINAL_CALL_RECORDING_STATUSES } from 'src/logic-functions/consta import { TWENTY_PAGE_SIZE } from 'src/logic-functions/constants/twenty-page-size'; import { type FilesFieldValue } from 'src/logic-functions/types/files-field-value.type'; import { - extractRecallBotConvergence, - type RecallBotConvergence, -} from 'src/logic-functions/recall-api/extract-recall-bot-convergence.util'; + extractRecallBotSyncState, + type RecallBotSyncState, +} from 'src/logic-functions/recall-api/extract-recall-bot-sync-state.util'; import { fetchAllNodes, type ConnectionPage, } from 'src/logic-functions/data/fetch-all-nodes.util'; import { getRecallBot } from 'src/logic-functions/recall-api/get-recall-bot.util'; -import { ingestCallRecordingMedia } from 'src/logic-functions/flows/ingest-call-recording-media.util'; +import { importCallRecordingMedia } from 'src/logic-functions/flows/import-call-recording-media.util'; import { isCallRecordingStatusDowngrade } from 'src/logic-functions/domain/is-call-recording-status-downgrade.util'; import { isNonEmptyString } from 'src/logic-functions/utils/is-non-empty-string.util'; import { parseTranscriptMarker } from 'src/logic-functions/domain/parse-transcript-marker.util'; import { persistCallRecordingProgress } from 'src/logic-functions/flows/persist-call-recording-progress.util'; -import { reconcileCallRecordingTranscriptArtifact } from 'src/logic-functions/flows/reconcile-call-recording-transcript-artifact.util'; +import { importCallRecordingTranscript } from 'src/logic-functions/flows/import-call-recording-transcript.util'; import { type ConvergeDivergedCallRecordingsResult } from 'src/logic-functions/flows/converge-diverged-call-recordings-result.type'; -import { shouldCompleteCallRecordingIngestion } from 'src/logic-functions/domain/should-complete-call-recording-ingestion.util'; +import { shouldCompleteCallRecordingImport } from 'src/logic-functions/domain/should-complete-call-recording-import.util'; import { updateCallRecording } from 'src/logic-functions/data/update-call-recording.util'; import { type CallRecordingUpdateFields } from 'src/logic-functions/types/call-recording-update-fields.type'; @@ -251,7 +251,7 @@ const convergeCallRecording = async ({ return; } - const convergence = extractRecallBotConvergence(botResult.bot); + const convergence = extractRecallBotSyncState(botResult.bot); const updateData = buildConvergenceFieldUpdates({ candidate, convergence }); const externalRecordingId = @@ -259,7 +259,7 @@ const convergeCallRecording = async ({ if (convergence.isRecallRecordingDone && !isUndefined(externalRecordingId)) { const transcriptArtifactResult = - await reconcileCallRecordingTranscriptArtifact({ + await importCallRecordingTranscript({ callRecordingId: candidate.id, currentStatus: candidate.status, externalRecordingId, @@ -273,7 +273,7 @@ const convergeCallRecording = async ({ result.requestedTranscriptCallRecordingIds.push(candidate.id); } - const mediaIngestionUpdate = await ingestCallRecordingMedia({ + const mediaIngestionUpdate = await importCallRecordingMedia({ callRecordingId: candidate.id, externalRecordingId, hasAudio: isNonEmptyArray(candidate.audio), @@ -299,12 +299,12 @@ const convergeCallRecording = async ({ Object.assign(updateData, terminalArtifactGateFailureUpdate); } - const completesIngestion = shouldCompleteCallRecordingIngestion({ + const completesImport = shouldCompleteCallRecordingImport({ current: candidate, updateData, }); - if (Object.keys(updateData).length === 0 && !completesIngestion) { + if (Object.keys(updateData).length === 0 && !completesImport) { return; } @@ -323,7 +323,7 @@ const buildConvergenceFieldUpdates = ({ convergence, }: { candidate: DivergedCallRecordingCandidate; - convergence: RecallBotConvergence; + convergence: RecallBotSyncState; }): CallRecordingUpdateFields => { const updateData: CallRecordingUpdateFields = {}; @@ -373,7 +373,7 @@ const buildTerminalArtifactGateFailureUpdate = ({ updateData, }: { candidate: DivergedCallRecordingCandidate; - convergence: RecallBotConvergence; + convergence: RecallBotSyncState; externalRecordingId: string | undefined; updateData: CallRecordingUpdateFields; }): TerminalArtifactGateFailureUpdate | undefined => { diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/handle-recall-webhook.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/handle-recall-webhook.util.ts index d6e332b910..347fb2311d 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/handle-recall-webhook.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/handle-recall-webhook.util.ts @@ -6,10 +6,10 @@ import { type FilesFieldValue } from 'src/logic-functions/types/files-field-valu import { buildFailedTranscriptMarker } from 'src/logic-functions/domain/build-failed-transcript-marker.util'; import { buildTranscriptFailureReason } from 'src/logic-functions/domain/build-transcript-failure-reason.util'; import { downloadTranscript } from 'src/logic-functions/flows/download-transcript.util'; -import { extractRecallBotConvergence } from 'src/logic-functions/recall-api/extract-recall-bot-convergence.util'; +import { extractRecallBotSyncState } from 'src/logic-functions/recall-api/extract-recall-bot-sync-state.util'; import { getRecallBot } from 'src/logic-functions/recall-api/get-recall-bot.util'; import { getString } from 'src/logic-functions/utils/get-string.util'; -import { ingestCallRecordingMedia } from 'src/logic-functions/flows/ingest-call-recording-media.util'; +import { importCallRecordingMedia } from 'src/logic-functions/flows/import-call-recording-media.util'; import { isCallRecordingStatusDowngrade } from 'src/logic-functions/domain/is-call-recording-status-downgrade.util'; import { isRecallRecordingDoneSignal } from 'src/logic-functions/domain/is-recall-recording-done-signal.util'; import { mapRecallStatusCodeToCallRecordingStatus } from 'src/logic-functions/domain/map-recall-status-code-to-call-recording-status.util'; @@ -20,7 +20,7 @@ import { } from 'src/logic-functions/recall-api/parse-recall-webhook-event.util'; import { parseTranscriptMarker } from 'src/logic-functions/domain/parse-transcript-marker.util'; import { persistCallRecordingProgress } from 'src/logic-functions/flows/persist-call-recording-progress.util'; -import { reconcileCallRecordingTranscriptArtifact } from 'src/logic-functions/flows/reconcile-call-recording-transcript-artifact.util'; +import { importCallRecordingTranscript } from 'src/logic-functions/flows/import-call-recording-transcript.util'; import { updateCallRecording } from 'src/logic-functions/data/update-call-recording.util'; import { type CallRecordingUpdateFields } from 'src/logic-functions/types/call-recording-update-fields.type'; @@ -197,16 +197,16 @@ const handleRecallStatusEvent = async ({ callRecordingStatus, }); - const mediaIngestionUpdate = await buildMediaIngestionUpdate({ + const mediaImportUpdate = await buildMediaImportUpdate({ callRecording, externalRecordingId: externalRecordingIdResolution.externalRecordingId, }); if (updateData.status === CallRecordingStatus.FAILED) { - delete mediaIngestionUpdate.callRecorderFailureReason; + delete mediaImportUpdate.callRecorderFailureReason; } - Object.assign(updateData, mediaIngestionUpdate); + Object.assign(updateData, mediaImportUpdate); const terminalArtifactGateFailureUpdate = buildTerminalArtifactGateFailureUpdate({ @@ -222,7 +222,7 @@ const handleRecallStatusEvent = async ({ } } - const { completesIngestion } = await persistCallRecordingProgress(client, { + const { completesImport } = await persistCallRecordingProgress(client, { id: callRecording.id, current: callRecording, updateData, @@ -234,7 +234,7 @@ const handleRecallStatusEvent = async ({ webhookEvent, callRecording, updateData, - callRecordingStatus: completesIngestion + callRecordingStatus: completesImport ? CallRecordingStatus.COMPLETED : (updateData.status ?? callRecordingStatus), }); @@ -244,7 +244,7 @@ const handleRecallStatusEvent = async ({ status: 'updated', event, callRecordingId: callRecording.id, - callRecordingStatus: completesIngestion + callRecordingStatus: completesImport ? CallRecordingStatus.COMPLETED : (updateData.status ?? callRecordingStatus), }; @@ -539,7 +539,7 @@ const hasReachableTranscript = (transcript: unknown): boolean => { const isTranscriptUnset = (callRecording: MatchedCallRecording): boolean => isUndefined(callRecording.transcript); -const buildMediaIngestionUpdate = async ({ +const buildMediaImportUpdate = async ({ callRecording, externalRecordingId, }: { @@ -560,13 +560,13 @@ const buildMediaIngestionUpdate = async ({ if (isUndefined(externalRecordingId)) { console.warn( - `[call-recorder] cannot ingest media for call recording ${callRecording.id}: no Recall recording id available`, + `[call-recorder] cannot import media for call recording ${callRecording.id}: no Recall recording id available`, ); return {}; } - return ingestCallRecordingMedia({ + return importCallRecordingMedia({ callRecordingId: callRecording.id, externalRecordingId, hasAudio, @@ -590,7 +590,7 @@ const buildTranscriptArtifactUpdate = async ({ } const transcriptArtifactResult = - await reconcileCallRecordingTranscriptArtifact({ + await importCallRecordingTranscript({ callRecordingId: callRecording.id, currentStatus: callRecording.status, externalRecordingId, @@ -641,7 +641,7 @@ const fetchExternalRecordingIdFromRecallBot = async ( } return { - externalRecordingId: extractRecallBotConvergence(botResult.bot) + externalRecordingId: extractRecallBotSyncState(botResult.bot) .externalRecordingId, providerLookupFailed: false, }; diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/ingest-call-recording-media.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-media.util.ts similarity index 87% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/ingest-call-recording-media.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-media.util.ts index 144ad7ee08..c28c9512ec 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/ingest-call-recording-media.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-media.util.ts @@ -20,8 +20,8 @@ type CallRecordingMediaUpdateFields = Pick< 'audio' | 'video' | 'callRecorderFailureReason' >; -type IngestMediaArtifactResult = - | { outcome: 'ingested'; files: CallRecordingMediaFile[] } +type ImportMediaArtifactResult = + | { outcome: 'imported'; files: CallRecordingMediaFile[] } | { outcome: 'too-large' } | { outcome: 'failed' }; @@ -38,7 +38,7 @@ type MediaUploadTarget = { const MEDIA_DOWNLOAD_TIMEOUT_MS = 120_000; const MEDIA_FILE_FOLDER = 'FilesField'; -export const ingestCallRecordingMedia = async ({ +export const importCallRecordingMedia = async ({ callRecordingId, externalRecordingId, hasAudio, @@ -57,7 +57,7 @@ export const ingestCallRecordingMedia = async ({ if (!recordingResult.ok) { console.warn( - `[call-recorder] failed to fetch Recall recording ${externalRecordingId} while ingesting media for call recording ${callRecordingId}: ${recordingResult.errorMessage}`, + `[call-recorder] failed to fetch Recall recording ${externalRecordingId} while importing media for call recording ${callRecordingId}: ${recordingResult.errorMessage}`, ); return {}; @@ -71,7 +71,7 @@ export const ingestCallRecordingMedia = async ({ const tooLargeFailureReasons: string[] = []; if (!hasVideo && !isUndefined(mediaUrls.videoUrl)) { - const video = await ingestMediaArtifact({ + const video = await importMediaArtifact({ callRecordingId, metadataClient, url: mediaUrls.videoUrl, @@ -81,7 +81,7 @@ export const ingestCallRecordingMedia = async ({ maxMediaFileSizeBytes, }); - if (video.outcome === 'ingested') { + if (video.outcome === 'imported') { updateFields.video = video.files; } @@ -91,7 +91,7 @@ export const ingestCallRecordingMedia = async ({ } if (!hasAudio && !isUndefined(mediaUrls.audioUrl)) { - const audio = await ingestMediaArtifact({ + const audio = await importMediaArtifact({ callRecordingId, metadataClient, url: mediaUrls.audioUrl, @@ -101,7 +101,7 @@ export const ingestCallRecordingMedia = async ({ maxMediaFileSizeBytes, }); - if (audio.outcome === 'ingested') { + if (audio.outcome === 'imported') { updateFields.audio = audio.files; } @@ -117,7 +117,7 @@ export const ingestCallRecordingMedia = async ({ return updateFields; }; -const ingestMediaArtifact = async ({ +const importMediaArtifact = async ({ callRecordingId, metadataClient, url, @@ -131,7 +131,7 @@ const ingestMediaArtifact = async ({ fileName: string; fieldMetadataUniversalIdentifier: string; maxMediaFileSizeBytes: number; -}): Promise => { +}): Promise => { try { const download = await openMediaDownload({ callRecordingId, @@ -142,7 +142,7 @@ const ingestMediaArtifact = async ({ if (download.outcome === 'too-large') { console.warn( - `[call-recorder] media-ingestion phase=artifact-too-large callRecordingId=${callRecordingId} fileName=${fileName} sizeBytes=${download.sizeBytes} maxMediaFileSizeBytes=${maxMediaFileSizeBytes}`, + `[call-recorder] media-import phase=artifact-too-large callRecordingId=${callRecordingId} fileName=${fileName} sizeBytes=${download.sizeBytes} maxMediaFileSizeBytes=${maxMediaFileSizeBytes}`, ); return { outcome: 'too-large' }; @@ -158,12 +158,12 @@ const ingestMediaArtifact = async ({ }); return { - outcome: 'ingested', + outcome: 'imported', files: [{ fileId, label: fileName }], }; } catch (error) { console.warn( - `[call-recorder] failed to ingest ${fileName} for call recording ${callRecordingId}: ${error instanceof Error ? error.message : String(error)}`, + `[call-recorder] failed to import ${fileName} for call recording ${callRecordingId}: ${error instanceof Error ? error.message : String(error)}`, ); return { outcome: 'failed' }; @@ -189,7 +189,7 @@ const openMediaDownload = async ({ ); console.log( - `[call-recorder] media-ingestion phase=artifact-download-response callRecordingId=${callRecordingId} fileName=${fileName} responseStatus=${response.status} contentLengthBytes=${contentLengthBytes ?? 'unknown'} ${formatMemoryUsageForLog()}`, + `[call-recorder] media-import phase=artifact-download-response callRecordingId=${callRecordingId} fileName=${fileName} responseStatus=${response.status} contentLengthBytes=${contentLengthBytes ?? 'unknown'} ${formatMemoryUsageForLog()}`, ); if (!response.ok) { @@ -260,7 +260,7 @@ const uploadMediaStreamToStorage = async ({ }); console.log( - `[call-recorder] media-ingestion phase=artifact-upload-start callRecordingId=${callRecordingId} fileName=${fileName} declaredBytes=${sizeBytes} ${formatMemoryUsageForLog()}`, + `[call-recorder] media-import phase=artifact-upload-start callRecordingId=${callRecordingId} fileName=${fileName} declaredBytes=${sizeBytes} ${formatMemoryUsageForLog()}`, ); await putMediaDownloadBodyToUploadTarget({ @@ -288,7 +288,7 @@ const cancelMediaDownloadBody = async ({ await body.cancel().catch((error) => { console.warn( - `[call-recorder] media-ingestion phase=download-body-cancel-failed callRecordingId=${callRecordingId} fileName=${fileName}: ${error instanceof Error ? error.message : String(error)}`, + `[call-recorder] media-import phase=download-body-cancel-failed callRecordingId=${callRecordingId} fileName=${fileName}: ${error instanceof Error ? error.message : String(error)}`, ); }); }; diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recording-transcript-artifact-result.type.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-transcript-result.type.ts similarity index 84% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recording-transcript-artifact-result.type.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-transcript-result.type.ts index 1c8ab638e4..7d8cfee05f 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recording-transcript-artifact-result.type.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-transcript-result.type.ts @@ -5,7 +5,7 @@ type CallRecordingTranscriptArtifactUpdateFields = Pick< 'callRecorderFailureReason' | 'status' | 'transcript' >; -export type ReconcileCallRecordingTranscriptArtifactResult = { +export type ImportCallRecordingTranscriptResult = { updateData: CallRecordingTranscriptArtifactUpdateFields; requestedTranscript: boolean; }; diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recording-transcript-artifact.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-transcript.util.ts similarity index 93% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recording-transcript-artifact.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-transcript.util.ts index c50aa5e8f9..3dbdc0f456 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recording-transcript-artifact.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-transcript.util.ts @@ -10,12 +10,12 @@ import { createAsyncRecallTranscript } from 'src/logic-functions/recall-api/crea import { listRecallTranscripts } from 'src/logic-functions/recall-api/list-recall-transcripts.util'; import { type RecallTranscriptSummary } from 'src/logic-functions/recall-api/recall-transcript-summary.type'; import { downloadTranscript } from 'src/logic-functions/flows/download-transcript.util'; -import { type ReconcileCallRecordingTranscriptArtifactResult } from 'src/logic-functions/flows/reconcile-call-recording-transcript-artifact-result.type'; +import { type ImportCallRecordingTranscriptResult } from 'src/logic-functions/flows/import-call-recording-transcript-result.type'; type CallRecordingTranscriptArtifactUpdateFields = - ReconcileCallRecordingTranscriptArtifactResult['updateData']; + ImportCallRecordingTranscriptResult['updateData']; -export const reconcileCallRecordingTranscriptArtifact = async ({ +export const importCallRecordingTranscript = async ({ callRecordingId, currentStatus, externalRecordingId, @@ -27,7 +27,7 @@ export const reconcileCallRecordingTranscriptArtifact = async ({ externalRecordingId: string; requestedAt: string; transcript: unknown; -}): Promise => { +}): Promise => { const existingTranscriptMarker = parseTranscriptMarker(transcript); if ( @@ -149,7 +149,7 @@ export const reconcileCallRecordingTranscriptArtifact = async ({ }; const buildEmptyTranscriptArtifactResult = - (): ReconcileCallRecordingTranscriptArtifactResult => ({ + (): ImportCallRecordingTranscriptResult => ({ updateData: {}, requestedTranscript: false, }); diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/persist-call-recording-progress.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/persist-call-recording-progress.util.ts index 17fab2679a..b7ba7041bc 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/persist-call-recording-progress.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/persist-call-recording-progress.util.ts @@ -2,7 +2,7 @@ import { type CoreApiClient } from 'twenty-client-sdk/core'; import { type FilesFieldValue } from 'src/logic-functions/types/files-field-value.type'; import { completeAndChargeCallRecording } from 'src/logic-functions/flows/complete-and-charge-call-recording.util'; -import { shouldCompleteCallRecordingIngestion } from 'src/logic-functions/domain/should-complete-call-recording-ingestion.util'; +import { shouldCompleteCallRecordingImport } from 'src/logic-functions/domain/should-complete-call-recording-import.util'; import { updateCallRecording } from 'src/logic-functions/data/update-call-recording.util'; import { type CallRecordingUpdateFields } from 'src/logic-functions/types/call-recording-update-fields.type'; @@ -27,16 +27,16 @@ export const persistCallRecordingProgress = async ( current: PersistCallRecordingProgressCurrent; updateData: CallRecordingUpdateFields; }, -): Promise<{ completesIngestion: boolean }> => { - const completesIngestion = shouldCompleteCallRecordingIngestion({ +): Promise<{ completesImport: boolean }> => { + const completesImport = shouldCompleteCallRecordingImport({ current, updateData, }); - if (!completesIngestion) { + if (!completesImport) { await updateCallRecording(client, { id, data: updateData }); - return { completesIngestion: false }; + return { completesImport: false }; } // Strip status so COMPLETED is written only by the atomic claim — its single winner bills once. @@ -54,5 +54,5 @@ export const persistCallRecordingProgress = async ( endedAt: updateData.endedAt ?? current.endedAt, }); - return { completesIngestion: true }; + return { completesImport: true }; }; diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recorder.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recorder.util.ts index 9767a6f3a8..2f9302bb1e 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recorder.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/reconcile-call-recorder.util.ts @@ -16,7 +16,7 @@ import { createCallRecording, type ScheduledCallRecordingFields, } from 'src/logic-functions/data/create-call-recording.util'; -import { ensureCallRecorder } from 'src/logic-functions/flows/ensure-call-recorder.util'; +import { scheduleRecallBotForCallRecording } from 'src/logic-functions/flows/schedule-recall-bot-for-call-recording.util'; import { fetchCalendarEventsByIds } from 'src/logic-functions/data/fetch-calendar-events-by-ids.util'; import { fetchCalendarEventsByStartsAtValues } from 'src/logic-functions/data/fetch-calendar-events-by-starts-at-values.util'; import { findCallRecordingsByCalendarEventIds } from 'src/logic-functions/data/find-call-recordings-by-calendar-event-ids.util'; @@ -345,7 +345,7 @@ const createPolicyManagedCallRecording = async ({ } // Winning the deterministic-id insert elects this run as the single writer that creates the bot. - const didScheduleBot = await ensureCallRecorder(client, { + const didScheduleBot = await scheduleRecallBotForCallRecording(client, { callRecording: { id: callRecordingId, ...scheduledFields, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/ensure-call-recorder.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/schedule-recall-bot-for-call-recording.util.ts similarity index 98% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/ensure-call-recorder.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/schedule-recall-bot-for-call-recording.util.ts index 459f9d3d46..08974ced45 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/ensure-call-recorder.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/schedule-recall-bot-for-call-recording.util.ts @@ -12,7 +12,7 @@ import { scheduleRecallBot } from 'src/logic-functions/recall-api/schedule-recal import { updateCallRecording } from 'src/logic-functions/data/update-call-recording.util'; // The sole place a Recall bot is created. Only the deterministic-create winner and the stale-state cron call it, so one writer per meeting POSTs exactly one bot. -export const ensureCallRecorder = async ( +export const scheduleRecallBotForCallRecording = async ( client: CoreApiClient, { callRecording, calendarEvent }: MeetingRecording, ): Promise => { diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/heal-call-recordings-missing-bot.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/schedule-recall-bots-for-pending-call-recordings.util.ts similarity index 78% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/flows/heal-call-recordings-missing-bot.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/flows/schedule-recall-bots-for-pending-call-recordings.util.ts index b550619da2..abb93485bb 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/heal-call-recordings-missing-bot.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/schedule-recall-bots-for-pending-call-recordings.util.ts @@ -2,28 +2,28 @@ import { isUndefined } from '@sniptt/guards'; import { type CoreApiClient } from 'twenty-client-sdk/core'; import { type CalendarEventRecord } from 'src/logic-functions/types/calendar-event-record.type'; -import { ensureCallRecorder } from 'src/logic-functions/flows/ensure-call-recorder.util'; +import { scheduleRecallBotForCallRecording } from 'src/logic-functions/flows/schedule-recall-bot-for-call-recording.util'; import { fetchCalendarEventsByIds } from 'src/logic-functions/data/fetch-calendar-events-by-ids.util'; import { findOpenScheduledCallRecordings } from 'src/logic-functions/data/find-open-scheduled-call-recordings.util'; import { getUniqueSortedIds } from 'src/logic-functions/utils/get-unique-sorted-ids.util'; -export type HealCallRecordingsMissingBotResult = { +export type ScheduleRecallBotsForPendingCallRecordingsResult = { scheduledCallRecordingIds: string[]; }; // Closes the create-winner crash gap: a run that inserted the row but died before POSTing leaves a botless recording, and the cron is the single writer that re-POSTs it. -export const healCallRecordingsMissingBot = async ({ +export const scheduleRecallBotsForPendingCallRecordings = async ({ client, now, }: { client: CoreApiClient; now: Date; -}): Promise => { - const botlessCallRecordings = ( +}): Promise => { + const pendingCallRecordings = ( await findOpenScheduledCallRecordings(client) ).filter((callRecording) => isUndefined(callRecording.externalBotId)); - if (botlessCallRecordings.length === 0) { + if (pendingCallRecordings.length === 0) { return { scheduledCallRecordingIds: [] }; } @@ -32,7 +32,7 @@ export const healCallRecordingsMissingBot = async ({ await fetchCalendarEventsByIds( client, getUniqueSortedIds( - botlessCallRecordings.map( + pendingCallRecordings.map( (callRecording) => callRecording.calendarEventId, ), ), @@ -41,7 +41,7 @@ export const healCallRecordingsMissingBot = async ({ ); const scheduledCallRecordingIds: string[] = []; - for (const callRecording of botlessCallRecordings) { + for (const callRecording of pendingCallRecordings) { const calendarEvent = isUndefined(callRecording.calendarEventId) ? undefined : calendarEventsById.get(callRecording.calendarEventId); @@ -50,7 +50,7 @@ export const healCallRecordingsMissingBot = async ({ continue; } - const didScheduleCallRecorder = await ensureCallRecorder(client, { + const didScheduleCallRecorder = await scheduleRecallBotForCallRecording(client, { callRecording, calendarEvent, }); diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/__tests__/extract-recall-bot-convergence.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/__tests__/extract-recall-bot-sync-state.test.ts similarity index 74% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/__tests__/extract-recall-bot-convergence.test.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/__tests__/extract-recall-bot-sync-state.test.ts index 0b2ece8240..22ed42fa6c 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/__tests__/extract-recall-bot-convergence.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/__tests__/extract-recall-bot-sync-state.test.ts @@ -1,10 +1,10 @@ import { describe, expect, it } from 'vitest'; -import { extractRecallBotConvergence } from 'src/logic-functions/recall-api/extract-recall-bot-convergence.util'; +import { extractRecallBotSyncState } from 'src/logic-functions/recall-api/extract-recall-bot-sync-state.util'; -describe('extractRecallBotConvergence', () => { +describe('extractRecallBotSyncState', () => { it('maps the latest status change code to a call recording status', () => { - const convergence = extractRecallBotConvergence({ + const syncState = extractRecallBotSyncState({ status_changes: [ { code: 'joining_call', created_at: '2026-01-01T12:58:00.000Z' }, { code: 'in_call_recording', created_at: '2026-01-01T13:02:00.000Z' }, @@ -13,13 +13,13 @@ describe('extractRecallBotConvergence', () => { ], }); - // COMPLETED is reserved for full artifact ingestion, never bot state. - expect(convergence.status).toBe('PROCESSING'); - expect(convergence.isRecallRecordingDone).toBe(true); + // COMPLETED is reserved for full artifact import, never bot state. + expect(syncState.status).toBe('PROCESSING'); + expect(syncState.isRecallRecordingDone).toBe(true); }); it('uses created_at to find the latest status when Recall returns status changes out of order', () => { - const convergence = extractRecallBotConvergence({ + const syncState = extractRecallBotSyncState({ status_changes: [ { code: 'done', created_at: '2026-01-01T14:05:00.000Z' }, { code: 'joining_call', created_at: '2026-01-01T12:58:00.000Z' }, @@ -27,11 +27,11 @@ describe('extractRecallBotConvergence', () => { ], }); - expect(convergence.status).toBe('PROCESSING'); + expect(syncState.status).toBe('PROCESSING'); }); it('prefers recording-object timestamps over status change entries', () => { - const convergence = extractRecallBotConvergence({ + const syncState = extractRecallBotSyncState({ status_changes: [ { code: 'in_call_recording', created_at: '2026-01-01T13:02:30.000Z' }, { code: 'call_ended', created_at: '2026-01-01T14:00:30.000Z' }, @@ -45,7 +45,7 @@ describe('extractRecallBotConvergence', () => { ], }); - expect(convergence).toEqual({ + expect(syncState).toEqual({ status: 'PROCESSING', failureReason: undefined, startedAt: '2026-01-01T13:02:00.000Z', @@ -56,7 +56,7 @@ describe('extractRecallBotConvergence', () => { }); it('falls back to status change timestamps when recordings carry none', () => { - const convergence = extractRecallBotConvergence({ + const syncState = extractRecallBotSyncState({ status_changes: [ { code: 'in_call_recording', created_at: '2026-01-01T13:02:00.000Z' }, { code: 'call_ended', created_at: '2026-01-01T14:00:00.000Z' }, @@ -64,7 +64,7 @@ describe('extractRecallBotConvergence', () => { recordings: [{ id: 'recall-recording-1' }], }); - expect(convergence).toEqual({ + expect(syncState).toEqual({ status: 'PROCESSING', failureReason: undefined, startedAt: '2026-01-01T13:02:00.000Z', @@ -75,7 +75,7 @@ describe('extractRecallBotConvergence', () => { }); it('normalizes microsecond-precision Recall timestamps to millisecond ISO', () => { - const convergence = extractRecallBotConvergence({ + const syncState = extractRecallBotSyncState({ status_changes: [ { code: 'done', created_at: '2026-06-10T12:20:00.123456+00:00' }, ], @@ -88,12 +88,12 @@ describe('extractRecallBotConvergence', () => { ], }); - expect(convergence.startedAt).toBe('2026-06-10T11:02:28.281Z'); - expect(convergence.endedAt).toBe('2026-06-10T12:17:28.281Z'); + expect(syncState.startedAt).toBe('2026-06-10T11:02:28.281Z'); + expect(syncState.endedAt).toBe('2026-06-10T12:17:28.281Z'); }); it('returns nothing derivable from an empty bot response', () => { - expect(extractRecallBotConvergence({})).toEqual({ + expect(extractRecallBotSyncState({})).toEqual({ status: undefined, failureReason: undefined, startedAt: undefined, @@ -104,7 +104,7 @@ describe('extractRecallBotConvergence', () => { }); it('skips malformed status change entries', () => { - const convergence = extractRecallBotConvergence({ + const syncState = extractRecallBotSyncState({ status_changes: [ null, 'not-an-object', @@ -114,7 +114,7 @@ describe('extractRecallBotConvergence', () => { recordings: 'not-an-array', }); - expect(convergence).toEqual({ + expect(syncState).toEqual({ status: 'RECORDING', failureReason: undefined, startedAt: '2026-01-01T13:02:00.000Z', @@ -125,7 +125,7 @@ describe('extractRecallBotConvergence', () => { }); it('carries the failing Recall status code as the failure reason', () => { - const convergence = extractRecallBotConvergence({ + const syncState = extractRecallBotSyncState({ status_changes: [ { code: 'joining_call', created_at: '2026-01-01T12:58:00.000Z' }, { @@ -135,19 +135,19 @@ describe('extractRecallBotConvergence', () => { ], }); - expect(convergence.status).toBe('FAILED'); - expect(convergence.failureReason).toBe('recording_permission_denied'); + expect(syncState.status).toBe('FAILED'); + expect(syncState.failureReason).toBe('recording_permission_denied'); }); it('leaves the status undefined for unknown latest codes', () => { - const convergence = extractRecallBotConvergence({ + const syncState = extractRecallBotSyncState({ status_changes: [ { code: 'in_call_recording', created_at: '2026-01-01T13:02:00.000Z' }, { code: 'some_future_code', created_at: '2026-01-01T13:30:00.000Z' }, ], }); - expect(convergence.status).toBeUndefined(); - expect(convergence.startedAt).toBe('2026-01-01T13:02:00.000Z'); + expect(syncState.status).toBeUndefined(); + expect(syncState.startedAt).toBe('2026-01-01T13:02:00.000Z'); }); }); diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/extract-recall-bot-convergence.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/extract-recall-bot-sync-state.util.ts similarity index 97% rename from packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/extract-recall-bot-convergence.util.ts rename to packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/extract-recall-bot-sync-state.util.ts index d6af6a3a16..8ebc92e373 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/extract-recall-bot-convergence.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/extract-recall-bot-sync-state.util.ts @@ -6,7 +6,7 @@ import { getString } from 'src/logic-functions/utils/get-string.util'; import { mapRecallStatusCodeToCallRecordingStatus } from 'src/logic-functions/domain/map-recall-status-code-to-call-recording-status.util'; import { normalizeRecallTimestamp } from 'src/logic-functions/recall-api/normalize-recall-timestamp.util'; -export type RecallBotConvergence = { +export type RecallBotSyncState = { status: CallRecordingStatus | undefined; failureReason: string | undefined; startedAt: string | undefined; @@ -21,9 +21,9 @@ type RecallBotStatusChange = { }; // Derives the state a full webhook history would have produced from GET /bot. -export const extractRecallBotConvergence = ( +export const extractRecallBotSyncState = ( bot: Record, -): RecallBotConvergence => { +): RecallBotSyncState => { const statusChanges = extractStatusChanges(bot); const latestStatusChange = getLatestStatusChange(statusChanges); const status = mapRecallStatusCodeToCallRecordingStatus( diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/recall-bot-api-request.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/recall-bot-api-request.util.ts index 62374f7f4b..9f91a4938f 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/recall-bot-api-request.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-api/recall-bot-api-request.util.ts @@ -25,7 +25,7 @@ type RecallBotApiRequestResult = errorMessage: string; }; -// Bot creates tolerate retries because duplicates stay unclaimed and get reaped. +// Bot creates tolerate retries because duplicates stay unclaimed and get canceled. // Callers that cannot retry idempotently can lower maxAttempts. export const recallBotApiRequest = async ( requestArgs: RecallBotApiRequestArgs, diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/reconcile-stale-bot-state.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/reconcile-stale-bot-state.ts index 23a7924fe6..8c77e997b8 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/reconcile-stale-bot-state.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/reconcile-stale-bot-state.ts @@ -6,17 +6,17 @@ import { STALE_BOT_STATE_CRON_PATTERN } from 'src/logic-functions/constants/stal import { convergeDivergedCallRecordings } from 'src/logic-functions/flows/converge-diverged-call-recordings.util'; import { type ConvergeDivergedCallRecordingsResult } from 'src/logic-functions/flows/converge-diverged-call-recordings-result.type'; import { - healCallRecordingsMissingBot, - type HealCallRecordingsMissingBotResult, -} from 'src/logic-functions/flows/heal-call-recordings-missing-bot.util'; + scheduleRecallBotsForPendingCallRecordings, + type ScheduleRecallBotsForPendingCallRecordingsResult, +} from 'src/logic-functions/flows/schedule-recall-bots-for-pending-call-recordings.util'; import { - reapOrphanedCallRecorders, - type ReapOrphanedCallRecordersResult, -} from 'src/logic-functions/flows/reap-orphaned-call-recorders.util'; + cleanupOrphanedRecallBots, + type CleanupOrphanedRecallBotsResult, +} from 'src/logic-functions/flows/cleanup-orphaned-recall-bots.util'; // Every unwanted bot passes through this join_at window before it can attend. -const REAPER_JOIN_AT_LOOKBACK_HOURS = 4; -const REAPER_JOIN_AT_LOOKAHEAD_HOURS = 24; +const CLEANUP_JOIN_AT_LOOKBACK_HOURS = 4; +const CLEANUP_JOIN_AT_LOOKAHEAD_HOURS = 24; type StepFailure = { error: string }; @@ -24,51 +24,51 @@ const reconcileStaleBotStateHandler = async (): Promise => { const now = new Date(); const client = new CoreApiClient(); - const botlessHealResult = await healCallRecordingsMissingBotSafely( + const pendingScheduleResult = await scheduleRecallBotsForPendingCallRecordingsSafely( client, now, ); - const orphanedBotReapingResult = - await reapOrphanedCallRecordersInJoinAtWindow(client, now); + const orphanedBotCleanupResult = + await cleanupOrphanedRecallBotsInJoinAtWindow(client, now); const statusConvergenceResult = await convergeDivergedCallRecordingsSafely( client, now, ); return { - botlessHealResult, - orphanedBotReapingResult, + pendingScheduleResult, + orphanedBotCleanupResult, statusConvergenceResult, }; }; -const healCallRecordingsMissingBotSafely = async ( +const scheduleRecallBotsForPendingCallRecordingsSafely = async ( client: CoreApiClient, now: Date, -): Promise => { +): Promise => { try { - return await healCallRecordingsMissingBot({ client, now }); + return await scheduleRecallBotsForPendingCallRecordings({ client, now }); } catch (error) { - return buildStepFailure('botless call recording healing', error); + return buildStepFailure('pending Recall bot scheduling', error); } }; -const reapOrphanedCallRecordersInJoinAtWindow = async ( +const cleanupOrphanedRecallBotsInJoinAtWindow = async ( client: CoreApiClient, now: Date, -): Promise => { +): Promise => { try { - return await reapOrphanedCallRecorders({ + return await cleanupOrphanedRecallBots({ client, joinAtAfter: new Date( - now.getTime() - REAPER_JOIN_AT_LOOKBACK_HOURS * 60 * 60 * 1000, + now.getTime() - CLEANUP_JOIN_AT_LOOKBACK_HOURS * 60 * 60 * 1000, ).toISOString(), joinAtBefore: new Date( - now.getTime() + REAPER_JOIN_AT_LOOKAHEAD_HOURS * 60 * 60 * 1000, + now.getTime() + CLEANUP_JOIN_AT_LOOKAHEAD_HOURS * 60 * 60 * 1000, ).toISOString(), }); } catch (error) { - return buildStepFailure('orphaned bot reaping', error); + return buildStepFailure('orphaned bot cancellation', error); } }; @@ -97,7 +97,7 @@ export default defineLogicFunction({ universalIdentifier: STALE_BOT_STATE_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER, name: 'reconcile-stale-bot-state', description: - 'Converges call recordings with Recall on a schedule: pulls stale bot statuses and overdue transcripts, finishes failed cancellations, schedules bots for recordings still missing one, and reaps unclaimed bots. Reads calendar events only to heal already-decided recordings, never to discover meetings.', + 'Converges call recordings with Recall on a schedule: pulls stale bot statuses and overdue transcripts, finishes failed cancellations, schedules bots for recordings still missing one, and cancels unclaimed bots. Reads calendar events only to repair already-decided recordings, never to discover meetings.', timeoutSeconds: 250, handler: reconcileStaleBotStateHandler, cronTriggerSettings: {