Skip to content

Comments

feat: call history V1#23761

Merged
keithwillcode merged 11 commits intomainfrom
feat/call-history
Sep 14, 2025
Merged

feat: call history V1#23761
keithwillcode merged 11 commits intomainfrom
feat/call-history

Conversation

@Udit-takkar
Copy link
Contributor

@Udit-takkar Udit-takkar commented Sep 10, 2025

What does this PR do?

Screenshot 2025-09-12 at 6 59 52 PM

Clicking on an item opens sheet with transcript, recording etc.

Screenshot 2025-09-12 at 6 49 48 PM

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • N/A I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • N/A I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 10, 2025

Walkthrough

Adds an Insights Call History feature spanning frontend, backend, types, and translations. Frontend: new /insights/call-history page with metadata and permission guard, a Call History table component (facets, search, filters, pagination), a Call Details sheet component, navigation entry, and English locale keys. Backend: new authed TRPC endpoint viewer.aiVoiceAgent.listCalls with Zod input schema and handler that resolves accessible phone numbers (memberships, phone-number repository), validates date filters, and delegates to an AI phone service. calAIPhone/Retell provider stack extended with listCalls types, SDK client method, service/provider wrappers, and a CallService implementation. Repository helpers for phone numbers and membership queries were added.

Possibly related PRs

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: call history V1" is concise and directly describes the primary change—adding a call history feature—so it accurately reflects the main intent of the changeset and is easy for teammates to scan.
Description Check ✅ Passed The PR description is on-topic and related to the changeset: it summarizes the call history UI, shows screenshots of the list and detail sheet, and includes the mandatory checklist, meeting the lenient requirements for this check.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 37ae02b and 22f0cd2.

📒 Files selected for processing (2)
  • apps/web/modules/insights/insights-call-history-view.tsx (1 hunks)
  • packages/features/ee/workflows/components/CallDetailsSheet.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/modules/insights/insights-call-history-view.tsx
  • packages/features/ee/workflows/components/CallDetailsSheet.tsx
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/call-history

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@keithwillcode keithwillcode added core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO labels Sep 10, 2025
@vercel
Copy link

vercel bot commented Sep 10, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Sep 12, 2025 4:57pm
cal-eu Ignored Ignored Sep 12, 2025 4:57pm

@Udit-takkar Udit-takkar changed the title feat: call history feat: call history V1 Sep 12, 2025
@Udit-takkar Udit-takkar marked this pull request as ready for review September 12, 2025 13:33
@graphite-app graphite-app bot requested a review from a team September 12, 2025 13:34
@dosubot dosubot bot added ai area: AI, cal.ai insights area: insights, analytics ✨ feature New feature or request labels Sep 12, 2025
@Udit-takkar Udit-takkar added the High priority Created by Linear-GitHub Sync label Sep 12, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/features/calAIPhone/providers/retellAI/index.ts (1)

66-86: Fix inconsistent listCalls typings — align to RetellCallListParams / RetellCallListResponse

  • Issue: Some layers use an inline param shape ({ userId, organizationId?, limit?, offset?, filters? }) while other parts use RetellCallListParams / RetellCallListResponse.
  • Locations to update/inspect:
    • packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts:369
    • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts:246
    • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts:294
    • packages/features/calAIPhone/providers/retellAI/types.ts:184
    • packages/features/calAIPhone/providers/retellAI/RetellSDKClient.ts:241
    • packages/features/calAIPhone/providers/retellAI/services/CallService.ts:290
  • Fix: Converge on RetellCallListParams/RetellCallListResponse across the interface, service, provider and SDK — either change signatures to those types or add a single adapter at the service boundary that maps the inline shape to RetellCallListParams before calling retellRepository.listCalls. Re-run type checks/CI.
🧹 Nitpick comments (21)
packages/lib/server/repository/membership.ts (1)

480-507: Clarify inclusion of unaccepted memberships and tighten types (optional)

This returns all memberships unless filters.accepted is provided. If call sites use this for auth scoping, you likely want accepted: true by default to avoid pulling pending invites.

If intentional, ignore. Otherwise, consider defaulting accepted to true at the caller or here.

Optionally, expose a typed return DTO to avoid accidental shape drift in consumers.

packages/features/calAIPhone/providers/retellAI/RetellAIService.ts (1)

246-254: Avoid any for filters; align with input schema

Use a precise type for filters to prevent accidental shape drift between TRPC input, services, and repository.

Apply this diff:

-  async listCalls(params: {
-    userId: number;
-    organizationId?: number;
-    limit?: number;
-    offset?: number;
-    filters?: any;
-  }) {
+  async listCalls(params: {
+    userId: number;
+    organizationId?: number;
+    limit?: number;
+    offset?: number;
+    filters?: { phoneNumberId?: string[]; startDate?: string; endDate?: string };
+  }) {
     return this.callService.listCalls(params);
   }
packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts (1)

294-302: Type filters and confirm org-level scope

Mirror the filter shape here to keep provider surface strongly typed. Also confirm that organizationId (not teamId) is the intended scope for listing calls.

Apply this diff:

-  async listCalls(params: {
-    userId: number;
-    organizationId?: number;
-    limit?: number;
-    offset?: number;
-    filters?: any;
-  }) {
+  async listCalls(params: {
+    userId: number;
+    organizationId?: number;
+    limit?: number;
+    offset?: number;
+    filters?: { phoneNumberId?: string[]; startDate?: string; endDate?: string };
+  }) {
     return await this.service.listCalls(params);
   }
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts (1)

1-15: Strengthen date validation and enforce start ≤ end (optional)

Tighten filters to catch malformed dates and inverted ranges at the boundary.

Apply this diff:

 export const ZListCallsInputSchema = z.object({
   limit: z.number().min(1).max(1000).default(50),
   offset: z.number().min(0).default(0),
   filters: z
     .object({
       phoneNumberId: z.array(z.string()).optional(),
-      startDate: z.string().optional(),
-      endDate: z.string().optional(),
+      startDate: z.string().datetime().optional(),
+      endDate: z.string().datetime().optional(),
     })
+    .refine(
+      (f) => !f?.startDate || !f?.endDate || new Date(f.startDate) <= new Date(f.endDate),
+      { message: "endDate must be on or after startDate" }
+    )
     .optional(),
 });
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (1)

67-74: Honor phone number filter from input.

filters.phoneNumberId exists in schema but isn’t applied. If intended to filter by specific numbers, apply it here; otherwise remove from schema to avoid dead params.

Apply:

-    const calls = await aiService.listCalls({
-      phoneNumbers: uniquePhoneNumbers,
+    const scopedPhoneNumbers =
+      input.filters?.phoneNumberId?.length
+        ? uniquePhoneNumbers.filter((pn) => input.filters!.phoneNumberId!.includes(pn))
+        : uniquePhoneNumbers;
+
+    const calls = await aiService.listCalls({
+      phoneNumbers: scopedPhoneNumbers,
       limit: input.limit,
       offset: input.offset,
       filters: {
         ...(startTimestamp && { startTimestamp }),
       },
     });

If phoneNumberId truly refers to DB IDs, map IDs → numbers via repository before filtering.

apps/web/app/(use-page-wrapper)/insights/call-history/page.tsx (1)

7-14: Remove redundant await in generateMetadata.

No need to await and then return; just return the promise.

-export const generateMetadata = async () =>
-  await _generateMetadata(
+export const generateMetadata = () =>
+  _generateMetadata(
     (t) => t("call_history"),
     (t) => t("call_history_subtitle"),
     undefined,
     undefined,
     "/insights/call-history"
   );
apps/web/modules/insights/insights-call-history-view.tsx (4)

16-16: Remove unused branding plumbing.

useOrgBranding() and _domain are unused; drop both to reduce noise.

-import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
@@
-function CallHistoryContent({ org: _org }: CallHistoryProps) {
-  const orgBranding = useOrgBranding();
-  const _domain = orgBranding?.fullDomain ?? WEBAPP_URL;
+function CallHistoryContent({ org: _org }: CallHistoryProps) {

Also applies to: 73-75


116-132: Handle missing timestamps in renderer.

Avoid creating Date("") or showing current time when data is absent.

-          const date = new Date(row.original.time);
-          return (
+          const hasTime = Boolean(row.original.time);
+          return hasTime ? (
+            (() => {
+              const date = new Date(row.original.time);
+              return (
+                <div className="text-sm">
+                  <div>{date.toLocaleDateString()}</div>
+                  <div className="text-subtle">{date.toLocaleTimeString()}</div>
+                </div>
+              );
+            })()
+          ) : (
             <div className="text-sm">
-              <div>{date.toLocaleDateString()}</div>
-              <div className="text-subtle">{date.toLocaleTimeString()}</div>
+              <div>{t("unknown")}</div>
             </div>
           );

169-181: Status mapping: add fallback instead of coercing to "failed".

Unknown provider statuses shouldn’t be labeled failed.

-          const status = row.original.sessionStatus;
+          const status = row.original.sessionStatus ?? "unknown";
-          const variant = status === "completed" ? "green" : status === "ongoing" ? "blue" : "red";
-          return <Badge variant={variant}>{status}</Badge>;
+          const variant = status === "completed" ? "green" : status === "ongoing" ? "blue" : status === "failed" ? "red" : "gray";
+          return <Badge variant={variant}>{status}</Badge>;

And in the mapping above:

-      sessionStatus:
-        call.call_status === "ended" ? "completed" : call.call_status === "ongoing" ? "ongoing" : "failed",
+      sessionStatus:
+        call.call_status === "ended"
+          ? "completed"
+          : call.call_status === "ongoing"
+            ? "ongoing"
+            : undefined,

251-273: Localize facet labels.

Hardcoded English breaks i18n. Use t() for labels.

-            { label: "Web Call", value: "web_call" },
-            { label: "Phone Call", value: "phone_call" },
+            { label: t("web_call"), value: "web_call" },
+            { label: t("phone_call"), value: "phone_call" },
@@
-            { label: "Completed", value: "completed" },
-            { label: "Ongoing", value: "ongoing" },
-            { label: "Failed", value: "failed" },
+            { label: t("completed"), value: "completed" },
+            { label: t("ongoing"), value: "ongoing" },
+            { label: t("failed"), value: "failed" },
@@
-            { label: "Positive", value: "positive" },
-            { label: "Neutral", value: "neutral" },
-            { label: "Negative", value: "negative" },
+            { label: t("positive"), value: "positive" },
+            { label: t("neutral"), value: "neutral" },
+            { label: t("negative"), value: "negative" },
packages/features/ee/workflows/components/CallDetailsSheet.tsx (2)

95-96: Localize "Unknown".

Avoid hardcoded English; use t("unknown") or similar.

-                  {selectedCall.call_analysis?.user_sentiment || "Unknown"}
+                  {selectedCall.call_analysis?.user_sentiment || t("unknown")}

115-126: Guard recording section and localize audio fallback.

Render recording only when a URL exists and localize the fallback text.

-          {/* Recording Section */}
-          <div className="space-y-3">
+          {/* Recording Section */}
+          {selectedCall.recording_url && (
+          <div className="space-y-3">
             <h3 className="text-emphasis text-base font-semibold">{t("recording")}</h3>
             <div className="flex items-center gap-3">
-              <audio controls className="h-11 w-[258px]" src={selectedCall.recording_url}>
-                Your browser does not support the audio element.
+              <audio controls className="h-11 w-[258px]" src={selectedCall.recording_url}>
+                {t("audio_not_supported")}
               </audio>
               <Button color="minimal" href={selectedCall.recording_url} target="_blank" StartIcon="download">
                 {t("download")}
               </Button>
             </div>
           </div>
+          )}
packages/lib/server/repository/calAiPhoneNumber.ts (1)

28-55: Dedup at the DB level and add explicit return types.

Small perf/readability wins:

  • Use Prisma distinct: ["phoneNumber"] in each finder to reduce duplicates early.
  • Add explicit return types for the helper methods.
 export class CalAiPhoneNumberRepository {
-  static async getUserPhoneNumbers(userId: number) {
+  static async getUserPhoneNumbers(
+    userId: number
+  ): Promise<Array<{ phoneNumber: string }>> {
     return prisma.calAiPhoneNumber.findMany({
       where: { userId },
       select: { phoneNumber: true },
+      distinct: ["phoneNumber"],
     });
   }
 
-  static async getOrganizationTeamPhoneNumbers(organizationId: number) {
+  static async getOrganizationTeamPhoneNumbers(
+    organizationId: number
+  ): Promise<Array<{ phoneNumber: string }>> {
     return prisma.calAiPhoneNumber.findMany({
       where: {
         team: { parentId: organizationId },
       },
       select: { phoneNumber: true },
+      distinct: ["phoneNumber"],
     });
   }
 
-  static async getTeamPhoneNumbers(teamIds: number[]) {
+  static async getTeamPhoneNumbers(
+    teamIds: number[]
+  ): Promise<Array<{ phoneNumber: string }>> {
     return prisma.calAiPhoneNumber.findMany({
       where: { teamId: { in: teamIds } },
       select: { phoneNumber: true },
+      distinct: ["phoneNumber"],
     });
   }
packages/features/ee/workflows/pages/call-history.tsx (8)

150-158: Localize channel type cell text.

Replace raw strings with t().

-        cell: ({ row }) => <span>{row.original.channelType}</span>,
+        cell: ({ row }) => (
+          <span>
+            {row.original.channelType === "web_call" ? t("web_call") : t("phone_call")}
+          </span>
+        ),

173-185: Localize session status badge text.

Use t() for user-visible strings.

-          return <Badge variant={variant}>{status}</Badge>;
+          return (
+            <Badge variant={variant}>
+              {status === "completed" ? t("completed") : status === "ongoing" ? t("ongoing") : t("failed")}
+            </Badge>
+          );

187-199: Localize sentiment badge text.

Replace with t().

-          return <Badge variant={variant}>{sentiment}</Badge>;
+          return (
+            <Badge variant={variant}>
+              {sentiment === "positive" ? t("positive") : sentiment === "negative" ? t("negative") : t("neutral")}
+            </Badge>
+          );

255-277: Localize facet filter labels.

These should use t() to match the rest of the UI.

         case "channelType":
           return convertFacetedValuesToMap([
-            { label: "Web Call", value: "web_call" },
-            { label: "Phone Call", value: "phone_call" },
+            { label: t("web_call"), value: "web_call" },
+            { label: t("phone_call"), value: "phone_call" },
           ]);
         case "sessionStatus":
           return convertFacetedValuesToMap([
-            { label: "Completed", value: "completed" },
-            { label: "Ongoing", value: "ongoing" },
-            { label: "Failed", value: "failed" },
+            { label: t("completed"), value: "completed" },
+            { label: t("ongoing"), value: "ongoing" },
+            { label: t("failed"), value: "failed" },
           ]);
         case "userSentiment":
           return convertFacetedValuesToMap([
-            { label: "Positive", value: "positive" },
-            { label: "Neutral", value: "neutral" },
-            { label: "Negative", value: "negative" },
+            { label: t("positive"), value: "positive" },
+            { label: t("neutral"), value: "neutral" },
+            { label: t("negative"), value: "negative" },
           ]);

74-83: Remove unused locals or document intentional side-effects.

  • _domain is unused.
  • _columnFilters and _searchTerm look intentionally unused for provider side-effects; add a short comment, or wire them to the query (see next comment).
-  const orgBranding = useOrgBranding();
-  const _domain = orgBranding?.fullDomain ?? WEBAPP_URL;
+  const orgBranding = useOrgBranding(); // branding may be used later; keep hook order
+  // const _domain = orgBranding?.fullDomain ?? WEBAPP_URL; // unused

238-255: Drop rowSelection plumbing since selection is disabled.

Simplify: remove local rowSelection state and related props.

-  const [rowSelection, setRowSelection] = useState({});
...
   const table = useReactTable({
     data: callHistoryData,
     columns,
     enableRowSelection: false,
     manualPagination: true,
-    state: {
-      rowSelection,
-    },
+    state: {},
...
-    onRowSelectionChange: setRowSelection,
-    getRowId: (row) => row.id,
+    getRowId: (row) => row.id,

Also applies to: 78-79


201-211: Use a matching translation key for the "to" header

en/common.json defines "to" (line 1728) while the "from" column uses "from_header" (line 2477). In packages/features/ee/workflows/pages/call-history.tsx (lines 201–211) either change t("to") → t("to_header") and add "to_header": "To" to the locales file, or keep t("to") to match the existing key — preferred: add "to_header" and use it.


84-94: Wire supported table filters to listCalls (schema supports phoneNumberId, startDate, endDate — not searchTerm/sorting)

  • Update packages/features/ee/workflows/pages/call-history.tsx (lines 84–94) to pass the table’s filter values into trpc.viewer.aiVoiceAgent.listCalls (use filters: { phoneNumberId, startDate, endDate }) instead of filters: {}.
  • If you need searchTerm or sorting, first extend ZListCallsInputSchema and listCalls.handler (and propagate to aiService.listCalls) to accept and implement those fields.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 74288b0 and 098ef94.

📒 Files selected for processing (20)
  • apps/web/app/(use-page-wrapper)/insights/call-history/page.tsx (1 hunks)
  • apps/web/modules/insights/insights-call-history-view.tsx (1 hunks)
  • apps/web/public/static/locales/en/common.json (3 hunks)
  • packages/features/calAIPhone/index.ts (1 hunks)
  • packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (2 hunks)
  • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts (1 hunks)
  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts (1 hunks)
  • packages/features/calAIPhone/providers/retellAI/RetellSDKClient.ts (2 hunks)
  • packages/features/calAIPhone/providers/retellAI/index.ts (3 hunks)
  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts (2 hunks)
  • packages/features/calAIPhone/providers/retellAI/types.ts (2 hunks)
  • packages/features/ee/workflows/components/CallDetailsSheet.tsx (1 hunks)
  • packages/features/ee/workflows/components/types.ts (1 hunks)
  • packages/features/ee/workflows/pages/call-history.tsx (1 hunks)
  • packages/features/shell/navigation/Navigation.tsx (1 hunks)
  • packages/lib/server/repository/calAiPhoneNumber.ts (1 hunks)
  • packages/lib/server/repository/membership.ts (1 hunks)
  • packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts (2 hunks)
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (1 hunks)
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts
  • packages/features/calAIPhone/providers/retellAI/types.ts
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
  • packages/lib/server/repository/membership.ts
  • packages/features/ee/workflows/components/types.ts
  • packages/lib/server/repository/calAiPhoneNumber.ts
  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts
  • packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts
  • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts
  • packages/features/calAIPhone/providers/retellAI/index.ts
  • packages/features/calAIPhone/index.ts
  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
  • packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts
  • packages/features/calAIPhone/providers/retellAI/RetellSDKClient.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts
  • packages/features/calAIPhone/providers/retellAI/types.ts
  • packages/features/shell/navigation/Navigation.tsx
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
  • packages/lib/server/repository/membership.ts
  • packages/features/ee/workflows/components/types.ts
  • packages/lib/server/repository/calAiPhoneNumber.ts
  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts
  • packages/features/ee/workflows/components/CallDetailsSheet.tsx
  • packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts
  • apps/web/modules/insights/insights-call-history-view.tsx
  • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts
  • packages/features/calAIPhone/providers/retellAI/index.ts
  • packages/features/calAIPhone/index.ts
  • apps/web/app/(use-page-wrapper)/insights/call-history/page.tsx
  • packages/features/ee/workflows/pages/call-history.tsx
  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
  • packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts
  • packages/features/calAIPhone/providers/retellAI/RetellSDKClient.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts
  • packages/features/calAIPhone/providers/retellAI/types.ts
  • packages/features/shell/navigation/Navigation.tsx
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
  • packages/lib/server/repository/membership.ts
  • packages/features/ee/workflows/components/types.ts
  • packages/lib/server/repository/calAiPhoneNumber.ts
  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts
  • packages/features/ee/workflows/components/CallDetailsSheet.tsx
  • packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts
  • apps/web/modules/insights/insights-call-history-view.tsx
  • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts
  • packages/features/calAIPhone/providers/retellAI/index.ts
  • packages/features/calAIPhone/index.ts
  • apps/web/app/(use-page-wrapper)/insights/call-history/page.tsx
  • packages/features/ee/workflows/pages/call-history.tsx
  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
  • packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts
  • packages/features/calAIPhone/providers/retellAI/RetellSDKClient.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/features/shell/navigation/Navigation.tsx
  • packages/features/ee/workflows/components/CallDetailsSheet.tsx
  • apps/web/modules/insights/insights-call-history-view.tsx
  • apps/web/app/(use-page-wrapper)/insights/call-history/page.tsx
  • packages/features/ee/workflows/pages/call-history.tsx
**/*Service.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

Files:

  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts
  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
🧠 Learnings (6)
📚 Learning: 2025-08-14T10:48:52.586Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/ai/_router.ts:46-84
Timestamp: 2025-08-14T10:48:52.586Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/ai/_router.ts, the voiceId input parameter in the create endpoint is intentionally not forwarded to aiService.createAgent() as voice customization is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.

Applied to files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
  • packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts
📚 Learning: 2025-08-08T10:26:13.362Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/providers/retellAI/services/PhoneNumberService.ts:212-220
Timestamp: 2025-08-08T10:26:13.362Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/providers/retellAI/services/PhoneNumberService.ts should include the phone number in client-facing HttpError messages (e.g., in updatePhoneNumber/getPhoneNumber catch blocks). Do not suggest redacting the phone number from these errors unless requirements change (per maintainer: Udit-takkar).

Applied to files:

  • packages/lib/server/repository/calAiPhoneNumber.ts
  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts
  • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts
  • packages/features/calAIPhone/providers/retellAI/index.ts
  • packages/features/calAIPhone/index.ts
📚 Learning: 2025-08-05T13:17:23.491Z
Learnt from: Udit-takkar
PR: calcom/cal.com#21827
File: packages/lib/server/repository/phoneNumber.ts:153-160
Timestamp: 2025-08-05T13:17:23.491Z
Learning: In the Cal.com Cal AI phone feature, the deletePhoneNumber repository method validation is properly handled in the service layer (RetellAIService.deletePhoneNumber) which validates user ownership and authorization before calling the repository method. The repository layer correctly focuses on data access only.

Applied to files:

  • packages/lib/server/repository/calAiPhoneNumber.ts
📚 Learning: 2025-08-08T09:29:11.681Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts:118-143
Timestamp: 2025-08-08T09:29:11.681Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (TypeScript), the AIPhoneServiceAgentListItem is required to include user.email in listAgents responses (per maintainer Udit-takkar). Future reviews should not flag this as unnecessary PII unless requirements change.

Applied to files:

  • packages/lib/server/repository/calAiPhoneNumber.ts
  • packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts
  • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts
  • packages/features/calAIPhone/providers/retellAI/index.ts
  • packages/features/calAIPhone/index.ts
  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
📚 Learning: 2025-08-27T12:15:43.830Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts:41-44
Timestamp: 2025-08-27T12:15:43.830Z
Learning: In calcom/cal.com, the AgentService.getAgent() method in packages/features/calAIPhone/providers/retellAI/services/AgentService.ts does NOT include authorization checks - it only validates the agentId parameter and directly calls the repository without verifying user/team access. This contrasts with other methods like getAgentWithDetails() which properly use findByIdWithUserAccessAndDetails() for authorization. When reviewing updateToolsFromAgentId() calls, always verify both agent ownership and eventType ownership are checked.

Applied to files:

  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts
📚 Learning: 2025-08-17T22:00:16.329Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts:117-126
Timestamp: 2025-08-17T22:00:16.329Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts, the enabled input parameter in the update endpoint is intentionally not forwarded to aiService.updateAgentConfiguration() as the enabled/disabled agent functionality is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.

Applied to files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts
🧬 Code graph analysis (12)
packages/features/calAIPhone/providers/retellAI/types.ts (2)
packages/features/calAIPhone/index.ts (2)
  • RetellCallListParams (48-48)
  • RetellCallListResponse (49-49)
packages/features/calAIPhone/providers/retellAI/index.ts (2)
  • RetellCallListParams (66-66)
  • RetellCallListResponse (67-67)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (4)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts (1)
  • TListCallsInputSchema (15-15)
packages/lib/server/repository/calAiPhoneNumber.ts (1)
  • CalAiPhoneNumberRepository (4-55)
packages/features/calAIPhone/index.ts (1)
  • createDefaultAIPhoneServiceProvider (22-22)
apps/api/v2/src/lib/logger.bridge.ts (1)
  • error (77-79)
packages/lib/server/repository/membership.ts (2)
packages/platform/libraries/index.ts (1)
  • MembershipRole (34-34)
packages/prisma/index.ts (1)
  • prisma (56-61)
packages/features/ee/workflows/components/types.ts (1)
packages/trpc/react/trpc.ts (1)
  • RouterOutputs (143-143)
packages/features/calAIPhone/providers/retellAI/services/CallService.ts (4)
packages/features/calAIPhone/index.ts (1)
  • RetellCallListResponse (49-49)
packages/features/calAIPhone/providers/retellAI/index.ts (1)
  • RetellCallListResponse (67-67)
packages/features/calAIPhone/providers/retellAI/types.ts (1)
  • RetellCallListResponse (14-14)
apps/api/v2/src/lib/logger.bridge.ts (1)
  • error (77-79)
packages/features/ee/workflows/components/CallDetailsSheet.tsx (1)
packages/features/ee/workflows/components/types.ts (2)
  • CallDetailsState (10-12)
  • CallDetailsAction (14-21)
apps/web/modules/insights/insights-call-history-view.tsx (7)
packages/trpc/react/trpc.ts (2)
  • RouterOutputs (143-143)
  • trpc (54-138)
packages/features/ee/workflows/components/types.ts (2)
  • CallDetailsState (10-12)
  • CallDetailsAction (14-21)
packages/features/data-table/DataTableProvider.tsx (1)
  • DataTableProvider (93-426)
packages/features/data-table/hooks/useSegments.ts (1)
  • useSegments (9-66)
packages/ui/components/test-setup.tsx (1)
  • useOrgBranding (37-39)
packages/lib/constants.ts (1)
  • WEBAPP_URL (12-18)
packages/features/ee/workflows/components/CallDetailsSheet.tsx (1)
  • CallDetailsSheet (23-182)
packages/features/calAIPhone/providers/retellAI/index.ts (2)
packages/features/calAIPhone/index.ts (2)
  • RetellCallListParams (48-48)
  • RetellCallListResponse (49-49)
packages/features/calAIPhone/providers/retellAI/types.ts (2)
  • RetellCallListParams (13-13)
  • RetellCallListResponse (14-14)
apps/web/app/(use-page-wrapper)/insights/call-history/page.tsx (3)
apps/web/app/_utils.tsx (1)
  • _generateMetadata (53-81)
apps/web/app/(use-page-wrapper)/insights/checkInsightsPagePermission.ts (1)
  • checkInsightsPagePermission (11-34)
apps/web/modules/insights/insights-call-history-view.tsx (1)
  • InsightsCallHistoryPage (316-320)
packages/features/ee/workflows/pages/call-history.tsx (8)
apps/web/modules/insights/insights-call-history-view.tsx (1)
  • CallHistoryProps (40-42)
packages/trpc/react/trpc.ts (2)
  • RouterOutputs (143-143)
  • trpc (54-138)
packages/features/ee/workflows/components/types.ts (2)
  • CallDetailsState (10-12)
  • CallDetailsAction (14-21)
packages/features/data-table/DataTableProvider.tsx (1)
  • DataTableProvider (93-426)
packages/features/data-table/hooks/useSegments.ts (1)
  • useSegments (9-66)
packages/ui/components/test-setup.tsx (1)
  • useOrgBranding (37-39)
packages/lib/constants.ts (1)
  • WEBAPP_URL (12-18)
packages/features/ee/workflows/components/CallDetailsSheet.tsx (1)
  • CallDetailsSheet (23-182)
packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts (2)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts (1)
  • ZListCallsInputSchema (3-13)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (1)
  • listCallsHandler (19-87)
packages/features/calAIPhone/providers/retellAI/RetellSDKClient.ts (2)
packages/features/calAIPhone/providers/retellAI/types.ts (2)
  • RetellCallListParams (13-13)
  • RetellCallListResponse (14-14)
apps/api/v2/src/lib/logger.bridge.ts (1)
  • error (77-79)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (17)
apps/web/public/static/locales/en/common.json (1)

2469-2477: No duplicate keys found — original comment is incorrect

Search of apps/web/public/static/locales/en/common.json shows each referenced call-history key appears exactly once (lines 2469–2477 and 3639–3678).

Likely an incorrect or invalid review comment.

packages/features/calAIPhone/index.ts (1)

48-50: LGTM: surfaced provider list-calls types

Type re-exports are consistent with existing patterns and keep consumers decoupled from provider internals.

packages/features/calAIPhone/providers/retellAI/RetellSDKClient.ts (1)

17-19: LGTM: SDK passthrough with useful logging

Method wiring to this.client.call.list looks good and logs avoid PII. Ensure the underlying SDK response is an array (current code logs response.length).

If Retell changes the response shape (e.g., to { calls, totalCount }), update the log line accordingly.

Also applies to: 241-259

packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts (2)

68-75: Good wiring for listCalls endpoint.

Dynamic import + authedProcedure pattern matches the rest of the router.


68-75: Verify we don't return provider secrets from listCalls

rg output shows no direct credential.key matches but does show provider API-key usage and provider responses that include access_token. Confirm that packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (and any provider methods it calls) do NOT forward: provider API keys, cal_api_key, credential.* fields, or long‑lived tokens to clients. Returning an ephemeral call access_token for a web‑call flow is OK only if intended and short‑lived. Check these files as starting points:

  • packages/features/calAIPhone/AIPhoneServiceRegistry.ts (resolveApiKey / provider config)
  • packages/features/calAIPhone/index.ts (example apiKey)
  • packages/features/calAIPhone/providers/retellAI/types.ts (createWebCall => access_token)
  • packages/features/calAIPhone/providers/retellAI/index.ts
  • packages/features/calAIPhone/providers/retellAI/RetellSDKClient.ts
  • packages/features/calAIPhone/providers/retellAI/services/AgentService.ts (cal_api_key usage)
packages/features/calAIPhone/providers/retellAI/types.ts (1)

12-15: Type aliases for Retell list calls look correct.

Clear, minimal surface tying to upstream SDK types.

packages/features/calAIPhone/providers/retellAI/index.ts (1)

22-24: Exports and provider type map extension are consistent.

Public types re-exported and TypeMap extended appropriately.

Also applies to: 66-86

packages/features/ee/workflows/components/types.ts (1)

1-21: UI types derived from TRPC outputs look good.

The CallData inference keeps frontend tightly bound to backend shape.

packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (1)

76-79: totalCount likely incorrect for pagination.

totalCount: calls.length returns the page size, not the dataset size. If the provider returns a total/has_more/next_offset, surface that instead; otherwise rename to pageCount.

Provide the provider response shape back to this handler or compute a separate totalCount.

apps/web/app/(use-page-wrapper)/insights/call-history/page.tsx (1)

16-20: LGTM: permission gate before render.

Server-side permission check runs before rendering the client view. Looks good.

apps/web/modules/insights/insights-call-history-view.tsx (1)

276-297: Resolve — prop name is correct: onRowMouseclick

DataTableWrapper and DataTable define and forward onRowMouseclick, so the handler will be invoked.

packages/features/calAIPhone/providers/retellAI/services/CallService.ts (1)

10-10: Confirm return type shape.

You import RetellCallListResponse and later return an array with extra field sessionOutcome. Ensure the alias actually represents an array of calls; otherwise responses will be structurally inconsistent across layers.

packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (2)

92-99: Type surface additions look good.

Generic aliases for ListCalls params/response are clear and keep the interface provider-agnostic.


366-375: Align org/team context naming across API.

Other methods use teamId; listCalls introduces organizationId. Confirm which is canonical to avoid consumer confusion.

packages/lib/server/repository/calAiPhoneNumber.ts (2)

6-9: Good: minimal Prisma selects (no include).

Only phoneNumber is selected per our guideline. This keeps responses lean and avoids sensitive fields.

Also applies to: 13-19, 22-25


12-26: Indexes already present in Prisma schema — no action required.
CalAiPhoneNumber has @@index([userId]) and @@index([teamId]) and Team has @@index([parentId]) in packages/prisma/schema.prisma.

packages/features/ee/workflows/pages/call-history.tsx (1)

320-326: Default export for a page is fine.

As this module represents a page component, keeping a default export aligns with our guideline exemptions.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/features/shell/navigation/Navigation.tsx (1)

125-130: Localization key looks correct; resolves prior feedback

Using the "call_history" i18n key is consistent with the rest of the menu and addresses the earlier comment.

🧹 Nitpick comments (2)
packages/features/shell/navigation/Navigation.tsx (1)

125-130: Optional: surface a phone icon for consistency with other entries

Uncommenting the icon improves visual scan and aligns with other nav items.

       {
         name: "call_history",
         href: "/insights/call-history",
-        // icon: "phone",
+        icon: "phone",
         isCurrent: ({ pathname: path }) => path?.startsWith("/insights/call-history") ?? false,
       },
packages/features/calAIPhone/providers/retellAI/RetellAIService.ts (1)

245-256: Add explicit Retell types to listCalls; skip local page-cap (CallService already defaults)

RetellCallListParams/RetellCallListResponse are exported from ./types — import and update the method signature. CallService already applies limit = 50, so no additional capping in RetellAIService is necessary. TRPC checks returned no credential.key or Prisma include leaks.

-  async listCalls(params: {
-    limit?: number;
-    offset?: number;
-    filters?: {
-      fromNumber: string[];
-      toNumber?: string[];
-      startTimestamp?: { lower_threshold?: number; upper_threshold?: number };
-    };
-  }) {
-    return this.callService.listCalls(params);
-  }
+  async listCalls(params: RetellCallListParams): Promise<RetellCallListResponse> {
+    return this.callService.listCalls(params);
+  }

Add import near existing ./types imports:

import type { RetellCallListParams, RetellCallListResponse } from "./types";
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 098ef94 and 7c8a011.

📒 Files selected for processing (8)
  • apps/web/modules/insights/insights-call-history-view.tsx (1 hunks)
  • packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (2 hunks)
  • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts (1 hunks)
  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts (1 hunks)
  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts (2 hunks)
  • packages/features/shell/navigation/Navigation.tsx (1 hunks)
  • packages/lib/server/repository/calAiPhoneNumber.ts (1 hunks)
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts
  • packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts
  • packages/features/calAIPhone/providers/retellAI/services/CallService.ts
  • packages/lib/server/repository/calAiPhoneNumber.ts
  • apps/web/modules/insights/insights-call-history-view.tsx
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*Service.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

Files:

  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
  • packages/features/shell/navigation/Navigation.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
  • packages/features/shell/navigation/Navigation.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/features/shell/navigation/Navigation.tsx
🧠 Learnings (3)
📚 Learning: 2025-08-08T09:29:11.681Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts:118-143
Timestamp: 2025-08-08T09:29:11.681Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (TypeScript), the AIPhoneServiceAgentListItem is required to include user.email in listAgents responses (per maintainer Udit-takkar). Future reviews should not flag this as unnecessary PII unless requirements change.

Applied to files:

  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
📚 Learning: 2025-08-08T10:26:13.362Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/providers/retellAI/services/PhoneNumberService.ts:212-220
Timestamp: 2025-08-08T10:26:13.362Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/providers/retellAI/services/PhoneNumberService.ts should include the phone number in client-facing HttpError messages (e.g., in updatePhoneNumber/getPhoneNumber catch blocks). Do not suggest redacting the phone number from these errors unless requirements change (per maintainer: Udit-takkar).

Applied to files:

  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
📚 Learning: 2025-08-08T09:27:23.896Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/providers/retellAI/services/AgentService.ts:195-216
Timestamp: 2025-08-08T09:27:23.896Z
Learning: In PR calcom/cal.com#22919, file packages/features/calAIPhone/providers/retellAI/services/AgentService.ts, the updateAgentConfiguration method intentionally does not persist the optional `name` parameter to the repository for now, per maintainer (Udit-takkar). Future reviews should not flag this unless requirements change.

Applied to files:

  • packages/features/calAIPhone/providers/retellAI/RetellAIService.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (2)

31-37: Guard org-scoped filters when organizationId is undefined

Prevent teams with parentId === undefined from slipping in when there’s no org context.

-    const adminTeamIds = userMemberships
-      .filter((m) => m.team?.parentId === organizationId)
-      .map((m) => m.teamId);
+    const adminTeamIds = organizationId
+      ? userMemberships
+          .filter((m) => m.team?.parentId === organizationId)
+          .map((m) => m.teamId)
+      : [];
 
-    const isOrgOwner = userMemberships.some(
-      (m) => m.role === MembershipRole.OWNER && m.team?.parentId === organizationId
-    );
+    const isOrgOwner =
+      !!organizationId &&
+      userMemberships.some(
+        (m) => m.role === MembershipRole.OWNER && m.team?.parentId === organizationId
+      );

83-91: Fix truthiness check; handle epoch 0 correctly

Using truthiness skips validation when a bound is exactly 0. Compare against undefined.

-      if (
-        startTimestamp.lower_threshold &&
-        startTimestamp.upper_threshold &&
-        startTimestamp.lower_threshold > startTimestamp.upper_threshold
-      ) {
+      if (
+        startTimestamp.lower_threshold !== undefined &&
+        startTimestamp.upper_threshold !== undefined &&
+        startTimestamp.lower_threshold > startTimestamp.upper_threshold
+      ) {
         throw new TRPCError({
           code: "BAD_REQUEST",
           message: "startDate must be before or equal to endDate",
         });
       }
🧹 Nitpick comments (2)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (2)

61-81: Prefer Number.isNaN over global isFinite for Date.parse validation

Date.parse returns a number or NaN; Number.isNaN is the precise check and avoids global-coercion semantics.

-        if (!isFinite(parsedStartDate)) {
+        if (Number.isNaN(parsedStartDate)) {
           throw new TRPCError({
             code: "BAD_REQUEST",
             message: "Invalid startDate format",
           });
         }
...
-        if (!isFinite(parsedEndDate)) {
+        if (Number.isNaN(parsedEndDate)) {
           throw new TRPCError({
             code: "BAD_REQUEST",
             message: "Invalid endDate format",
           });
         }

104-107: Sanitize outbound payload; never return secrets

Before returning, ensure the payload cannot contain credential.key or similar secret fields from downstream providers. Prefer an allowlist projection of fields needed by the UI.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7c8a011 and 39b5b20.

📒 Files selected for processing (2)
  • apps/web/modules/insights/insights-call-history-view.tsx (1 hunks)
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/modules/insights/insights-call-history-view.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
🧠 Learnings (2)
📚 Learning: 2025-08-14T10:48:52.586Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/ai/_router.ts:46-84
Timestamp: 2025-08-14T10:48:52.586Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/ai/_router.ts, the voiceId input parameter in the create endpoint is intentionally not forwarded to aiService.createAgent() as voice customization is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.

Applied to files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
📚 Learning: 2025-08-17T22:00:16.329Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts:117-126
Timestamp: 2025-08-17T22:00:16.329Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts, the enabled input parameter in the update endpoint is intentionally not forwarded to aiService.updateAgentConfiguration() as the enabled/disabled agent functionality is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.

Applied to files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
🧬 Code graph analysis (1)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (3)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts (1)
  • TListCallsInputSchema (15-15)
packages/lib/server/repository/calAiPhoneNumber.ts (1)
  • CalAiPhoneNumberRepository (3-54)
packages/features/calAIPhone/index.ts (1)
  • createDefaultAIPhoneServiceProvider (22-22)
🔇 Additional comments (2)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (2)

104-107: Confirm provider totalCount; avoid using page length as total

calls.length is the page size, not the dataset total. Use the provider's returned total (e.g., response.totalCount or response.meta.total) and return that instead of calls.length.

Location: packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (lines 104–107). Repo search found no listCalls/ListCalls signature — confirm the provider method and its return shape and update this handler to pass through provider.totalCount.


95-102: Enforce sane bounds for limit/offset

Cap limit (max 100) and ensure offset is an integer >= 0 in the Zod input schema (reject negatives or coerce to 0).

Location: packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (lines 95–102) — update ZListCallsInputSchema or add router-level validation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (4)

20-20: Fix unsafe access to profiles array.

ctx.user.profiles[0] can throw when profiles is undefined. Use optional chaining on the index.

-  const organizationId = ctx.user.organizationId ?? ctx.user.profiles[0]?.organizationId;
+  const organizationId = ctx.user.organizationId ?? ctx.user.profiles?.[0]?.organizationId;

31-33: Guard org-scoped adminTeamIds when organizationId is undefined.

Current logic includes all admin team IDs when no org context, potentially widening phone-number access beyond the intended org. Short-circuit to an empty array when organizationId is falsy.

-    const adminTeamIds = userMemberships
-      .filter((m) => !organizationId || m.team?.parentId === organizationId)
-      .map((m) => m.teamId);
+    const adminTeamIds = organizationId
+      ? userMemberships
+          .filter((m) => m.team?.parentId === organizationId)
+          .map((m) => m.teamId)
+      : [];

107-113: Preserve TRPCError status; don’t mask BAD_REQUEST with 500.

Re-throw known TRPCError and only wrap unknowns.

-  } catch (error) {
-    logger.error(`Failed to list calls for user ${ctx.user.id}:`, error);
-    throw new TRPCError({
-      code: "INTERNAL_SERVER_ERROR",
-      message: "Failed to retrieve call history",
-    });
-  }
+  } catch (error) {
+    if (error instanceof TRPCError) {
+      throw error;
+    }
+    logger.error(`Failed to list calls for user ${ctx.user.id}:`, error);
+    throw new TRPCError({
+      code: "INTERNAL_SERVER_ERROR",
+      message: "Failed to retrieve call history",
+    });
+  }

82-91: Fix truthiness check for date bounds.

Using truthiness skips the comparison when a timestamp is 0 (Unix epoch). Compare against undefined explicitly.

-      if (
-        startTimestamp.lower_threshold &&
-        startTimestamp.upper_threshold &&
-        startTimestamp.lower_threshold > startTimestamp.upper_threshold
-      ) {
+      if (
+        startTimestamp.lower_threshold !== undefined &&
+        startTimestamp.upper_threshold !== undefined &&
+        startTimestamp.lower_threshold > startTimestamp.upper_threshold
+      ) {
🧹 Nitpick comments (3)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (3)

54-54: Consider reusing/injecting the AI service provider.

If creation is non-trivial, initialize once at module scope or inject for easier testing and lower overhead.


103-106: totalCount should reflect the full result set if available.

If the provider returns a server-side total, surface it; otherwise document that totalCount equals the current page length.


95-96: Schema already enforces limit/offset; no handler clamp required.

ZListCallsInputSchema (packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts) defines limit: z.number().min(1).max(1000).default(50) and offset: z.number().min(0).default(0), so input.limit/input.offset are validated. Optional: add .int() and/or reduce max to 100 to tighten page-size caps.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 39b5b20 and 47803b5.

📒 Files selected for processing (1)
  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
🧠 Learnings (3)
📚 Learning: 2025-08-14T10:48:52.586Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/ai/_router.ts:46-84
Timestamp: 2025-08-14T10:48:52.586Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/ai/_router.ts, the voiceId input parameter in the create endpoint is intentionally not forwarded to aiService.createAgent() as voice customization is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.

Applied to files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
📚 Learning: 2025-08-17T22:00:16.329Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts:117-126
Timestamp: 2025-08-17T22:00:16.329Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts, the enabled input parameter in the update endpoint is intentionally not forwarded to aiService.updateAgentConfiguration() as the enabled/disabled agent functionality is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.

Applied to files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
📚 Learning: 2025-08-08T10:26:13.362Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/providers/retellAI/services/PhoneNumberService.ts:212-220
Timestamp: 2025-08-08T10:26:13.362Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/providers/retellAI/services/PhoneNumberService.ts should include the phone number in client-facing HttpError messages (e.g., in updatePhoneNumber/getPhoneNumber catch blocks). Do not suggest redacting the phone number from these errors unless requirements change (per maintainer: Udit-takkar).

Applied to files:

  • packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts
🧬 Code graph analysis (1)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (4)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.schema.ts (1)
  • TListCallsInputSchema (15-15)
packages/lib/server/repository/membership.ts (1)
  • MembershipRepository (84-532)
packages/lib/server/repository/calAiPhoneNumber.ts (1)
  • CalAiPhoneNumberRepository (3-54)
packages/features/calAIPhone/index.ts (1)
  • createDefaultAIPhoneServiceProvider (22-22)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests / Unit
  • GitHub Check: Type check / check-types
  • GitHub Check: Linters / lint
🔇 Additional comments (3)
packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts (3)

35-37: Owner check is correctly scoped.

isOrgOwner only evaluates truthy when organizationId is defined and matches the membership’s parent. Looks good.


46-52: Good early return when no accessible numbers.

Avoids an unnecessary provider call and keeps behavior explicit.


94-101: Confirm provider timestamp units; camelCase keys are already correct.

  • Confirmed: the interface and provider wrapper expect filters.fromNumber (string[]) and filters.startTimestamp { lower_threshold?: number; upper_threshold?: number } — see packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts and packages/features/calAIPhone/providers/retellAI/services/CallService.ts (which maps to filter_criteria.from_number / start_timestamp).
  • Observed: listCalls.handler sets startTimestamp using Date.parse(...) (milliseconds) and passes it straight through (packages/trpc/server/routers/viewer/aiVoiceAgent/listCalls.handler.ts).
  • Action: verify whether the external Retell SDK/API expects start_timestamp in seconds or milliseconds; if it expects seconds, convert ms -> s (Math.floor(ms / 1000)) before calling aiService.listCalls.

@github-actions
Copy link
Contributor

E2E results are ready!

roles?: MembershipRole[];
};
}) {
return prisma.membership.findMany({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a follow-up PR to add integration tests to ensure its functionality.

.filter((m) => !organizationId || m.team?.parentId === organizationId)
.map((m) => m.teamId);

const isOrgOwner = organizationId
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a follow up. This sort of logic should not live in this code. It shouldn't know how to calculate if this person if an org owner or not. That belongs in orgs code.

Copy link
Contributor

@keithwillcode keithwillcode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving since we have a tight timeframe but added a couple of follow-ups that need to happen ASAP.

@keithwillcode keithwillcode merged commit 4165042 into main Sep 14, 2025
64 of 65 checks passed
@keithwillcode keithwillcode deleted the feat/call-history branch September 14, 2025 15:04
@coderabbitai coderabbitai bot mentioned this pull request Sep 29, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai area: AI, cal.ai core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO ✨ feature New feature or request High priority Created by Linear-GitHub Sync insights area: insights, analytics ready-for-e2e size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants