Conversation
WalkthroughAdds full inbound-agent support and splits outbound-agent APIs: renames createAgent → createOutboundAgent and adds createInboundAgent across AI phone service interfaces, providers, SDKs, and tests. Converts Prisma repositories to instance-based classes, updates Prisma schema with Agent.inboundEventTypeId and WorkflowStep.inboundAgentId, and exposes linkOutboundAgentToWorkflow and linkInboundAgentToWorkflow. Adds TRPC routes/handlers for setting up and updating inbound agents, updates workflow handlers to track/delete inbound agents, introduces a new AgentConfigurationSheet and many agent-configuration UI/hooks/forms/utils/locales, and renames phone-number billing flag to disconnectAgents. Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
⏰ 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). (8)
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. Comment |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
packages/features/calAIPhone/providers/retellAI/RetellAIService.test.ts (1)
792-819: Add missing test coverage forcreateInboundAgent
No test blocks for the newly introducedcreateInboundAgentmethod were found; please add a corresponding test (mirroring thecreateOutboundAgentsuite) to ensure full coverage.packages/features/ee/billing/api/webhook/_customer.subscription.deleted.ts (1)
67-67: Update type annotation to reflect instance method usage.Line 67 references the static method signature
PrismaPhoneNumberRepository.findByStripeSubscriptionId, but the code now uses an instance method. While this likely works due to matching return types, update the type annotation for consistency.Apply this diff:
async function handleCalAIPhoneNumberSubscriptionDeleted( subscription: Data["object"], - phoneNumber: NonNullable<Awaited<ReturnType<typeof PrismaPhoneNumberRepository.findByStripeSubscriptionId>>> + phoneNumber: NonNullable<Awaited<ReturnType<PrismaPhoneNumberRepository["findByStripeSubscriptionId"]>>> ) {packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts (1)
47-52: CRITICAL: Verify eventType ownership before calling updateToolsFromAgentId.
updateToolsFromAgentIddoes not perform eventType authorization internally. Before line 47, verify thatinput.eventTypeIdbelongs toctx.user.idor the user's accessible teams (e.g., viaEventTypeRepository.findByIdWithUserAccess). Throw aTRPCErrorwith code"FORBIDDEN"if verification fails.Based on learnings and past review comments.
🤖 Prompt for AI Agents
In packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts around lines 21-52, before calling aiService.updateToolsFromAgentId at line 47, add eventType ownership verification: instantiate an EventTypeRepository, call a method like findByIdWithUserAccess with eventTypeId (from input.eventTypeId), userId (ctx.user.id), and teamId (input.teamId), and throw a TRPCError with code "FORBIDDEN" or "NOT_FOUND" if the eventType is not accessible; only after successful verification proceed to call updateToolsFromAgentId.packages/features/ee/billing/api/webhook/_checkout.session.completed.ts (1)
149-157: Remove duplicate agent fetch.The agent was already fetched and validated at lines 104-108 with the same parameters (
agentId,userId,teamId). Reuse the earlieragentvariable instead of querying again.Apply this diff:
try { console.log("Attempting to link agent to phone number:", { agentId, phoneNumberId: newNumber.id }); - const agent = await agentRepo.findByIdWithUserAccess({ - agentId, - userId, - }); - - if (!agent) { - console.error("Agent not found or user does not have access", { agentId, userId }); - throw new HttpCode(404, "Agent not found or user does not have access to it"); - } - + // Reuse agent from line 104 console.log("Found agent:", { agentId: agent.id, providerAgentId: agent.providerAgentId });
♻️ Duplicate comments (5)
packages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.ts (1)
43-48: CRITICAL: Verify eventType ownership before calling updateToolsFromAgentId.
updateToolsFromAgentIddoes not enforce eventType RBAC internally. A malicious user could point tools at an eventType they don't own, potentially causing API failures or unauthorized data access.Before line 43, fetch and verify that
eventTypeIdbelongs toctx.user.idor the user's accessible teams using an RBAC-aware repository method (e.g.,EventTypeRepository.findByIdWithUserAccess). Throw 403/404 if verification fails.Based on learnings and past review comments.
🤖 Prompt for AI Agents
In packages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.ts around lines 29-48, before calling aiService.updateToolsFromAgentId, add eventType ownership verification: instantiate an EventTypeRepository (or use an existing RBAC helper), call a method like findByIdWithUserAccess with eventTypeId, userId, and teamId (or similar parameters), and throw a TRPCError with code "FORBIDDEN" or "NOT_FOUND" if the eventType is not returned or user lacks access; only after successful verification proceed to call updateToolsFromAgentId.apps/web/app/api/webhooks/retell-ai/route.ts (1)
172-175: Missing import:prismais not defined.Line 172 creates
new PrismaPhoneNumberRepository(prisma), butprismais not imported. This will cause a runtime error.Add the missing import at the top of the file (same fix as above):
+import prisma from "@calcom/prisma"; import { CreditService } from "@calcom/features/ee/billing/credit-service";packages/lib/server/repository/PrismaAgentRepository.ts (1)
590-599: Validate eventTypeId ownership in service layer
- In updateInboundAgentEventTypeHandler, after confirming agent access via getAgentWithDetails, add a check that eventTypeId belongs to the same user/team (e.g. fetch the EventType with teamId and error if not found) before calling PrismaAgentRepository.updateEventTypeId.
packages/lib/server/repository/PrismaPhoneNumberRepository.ts (2)
436-464: Implementation looks correct; interface and caller updates pending.The dual-disconnect behavior for both inbound and outbound agents is correctly implemented. As noted in previous reviews, ensure the interface and callers are updated to use the new
disconnectAgentsparameter.
525-572: LGTM! Atomic set-if-unset correctly implemented.The method uses
updateManywith a conditional WHERE clause to atomically set the inbound agent only if unset. The count-based success check and fallback query for conflict detection are correct.
🧹 Nitpick comments (4)
packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts (1)
49-56: Consider using repository pattern for the update operation.While the query operation now uses the repository pattern (line 18), this update still directly calls
prisma.calAiPhoneNumber.update. For consistency and better separation of concerns, consider adding anupdateSubscriptionStatusmethod toPrismaPhoneNumberRepositoryand using it here.This would consolidate all phone number database operations in the repository:
const subscriptionStatus = statusMap[subscription.status] || PhoneNumberSubscriptionStatus.UNPAID; await phoneNumberRepo.updateSubscriptionStatus({ id: phoneNumber.id, status: subscriptionStatus, });And in
PrismaPhoneNumberRepository:async updateSubscriptionStatus({ id, status }: { id: number; status: PhoneNumberSubscriptionStatus }) { return await this.prismaClient.calAiPhoneNumber.update({ where: { id }, data: { subscriptionStatus: status }, }); }packages/features/ee/billing/api/webhook/_checkout.session.completed.ts (1)
167-174: Prefer repository method over direct Prisma call for consistency.Direct
prisma.calAiPhoneNumber.updatebypasses the repository abstraction. IfPrismaPhoneNumberRepositoryhas anupdateAgentsor similar method (as indicated in relevant code snippets), use it instead for consistency.If
phoneNumberRepo.updateAgentssupports linking agents, apply this pattern:- await prisma.calAiPhoneNumber.update({ - where: { id: newNumber.id }, - data: { - outboundAgent: { - connect: { id: agentId }, - }, - }, - }); + await phoneNumberRepo.updateAgents({ + id: newNumber.id, + outboundProviderAgentId: agent.providerAgentId, + });packages/features/calAIPhone/providers/adapters/PrismaAgentRepositoryAdapter.ts (1)
14-81: Consider reusing a single PrismaAgentRepository instance.Each method creates a new
PrismaAgentRepository(prisma)instance, which adds unnecessary object allocation overhead. Consider creating the repository once in the adapter constructor and reusing it across all methods.Apply this pattern:
export class PrismaAgentRepositoryAdapter implements AgentRepositoryInterface { + private agentRepo: PrismaAgentRepository; + + constructor() { + this.agentRepo = new PrismaAgentRepository(prisma); + } + async canManageTeamResources(params: { userId: number; teamId: number }): Promise<boolean> { - const agentRepo = new PrismaAgentRepository(prisma); - return await agentRepo.canManageTeamResources(params); + return await this.agentRepo.canManageTeamResources(params); }Then apply the same pattern to all other methods.
packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.ts (1)
14-89: Consider reusing a single PrismaPhoneNumberRepository instance.Similar to the agent adapter, each method creates a new
PrismaPhoneNumberRepository(prisma)instance. Consider creating the repository once in the constructor and reusing it.Apply this pattern:
export class PrismaPhoneNumberRepositoryAdapter implements PhoneNumberRepositoryInterface { + private phoneNumberRepo: PrismaPhoneNumberRepository; + + constructor() { + this.phoneNumberRepo = new PrismaPhoneNumberRepository(prisma); + } + async findByPhoneNumberAndUserId(params: { phoneNumber: string; userId: number; }): Promise<PhoneNumberData | null> { - const phoneNumberRepo = new PrismaPhoneNumberRepository(prisma); - return await phoneNumberRepo.findByPhoneNumberAndUserId(params); + return await this.phoneNumberRepo.findByPhoneNumberAndUserId(params); }Then apply to all other methods.
📜 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.
📒 Files selected for processing (14)
apps/web/app/api/webhooks/retell-ai/__tests__/route.test.ts(19 hunks)apps/web/app/api/webhooks/retell-ai/route.ts(2 hunks)packages/features/calAIPhone/providers/adapters/PrismaAgentRepositoryAdapter.ts(3 hunks)packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.ts(3 hunks)packages/features/calAIPhone/providers/retellAI/RetellAIService.test.ts(9 hunks)packages/features/calAIPhone/providers/retellAI/types.ts(4 hunks)packages/features/ee/billing/api/webhook/_checkout.session.completed.ts(4 hunks)packages/features/ee/billing/api/webhook/_customer.subscription.deleted.ts(2 hunks)packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts(2 hunks)packages/lib/server/repository/PrismaAgentRepository.ts(22 hunks)packages/lib/server/repository/PrismaPhoneNumberRepository.ts(19 hunks)packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts(2 hunks)packages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.ts(1 hunks)packages/trpc/server/routers/viewer/phoneNumber/list.handler.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/api/webhooks/retell-ai/tests/route.test.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.tspackages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.tspackages/features/ee/billing/api/webhook/_customer.subscription.updated.tspackages/features/ee/billing/api/webhook/_checkout.session.completed.tspackages/lib/server/repository/PrismaAgentRepository.tspackages/features/calAIPhone/providers/retellAI/types.tspackages/features/calAIPhone/providers/adapters/PrismaAgentRepositoryAdapter.tspackages/features/ee/billing/api/webhook/_customer.subscription.deleted.tspackages/features/calAIPhone/providers/retellAI/RetellAIService.test.tspackages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.tsapps/web/app/api/webhooks/retell-ai/route.tspackages/lib/server/repository/PrismaPhoneNumberRepository.tspackages/trpc/server/routers/viewer/phoneNumber/list.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/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.tspackages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.tspackages/features/ee/billing/api/webhook/_customer.subscription.updated.tspackages/features/ee/billing/api/webhook/_checkout.session.completed.tspackages/lib/server/repository/PrismaAgentRepository.tspackages/features/calAIPhone/providers/retellAI/types.tspackages/features/calAIPhone/providers/adapters/PrismaAgentRepositoryAdapter.tspackages/features/ee/billing/api/webhook/_customer.subscription.deleted.tspackages/features/calAIPhone/providers/retellAI/RetellAIService.test.tspackages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.tsapps/web/app/api/webhooks/retell-ai/route.tspackages/lib/server/repository/PrismaPhoneNumberRepository.tspackages/trpc/server/routers/viewer/phoneNumber/list.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/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.tspackages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.tspackages/features/ee/billing/api/webhook/_customer.subscription.updated.tspackages/features/ee/billing/api/webhook/_checkout.session.completed.tspackages/lib/server/repository/PrismaAgentRepository.tspackages/features/calAIPhone/providers/retellAI/types.tspackages/features/calAIPhone/providers/adapters/PrismaAgentRepositoryAdapter.tspackages/features/ee/billing/api/webhook/_customer.subscription.deleted.tspackages/features/calAIPhone/providers/retellAI/RetellAIService.test.tspackages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.tsapps/web/app/api/webhooks/retell-ai/route.tspackages/lib/server/repository/PrismaPhoneNumberRepository.tspackages/trpc/server/routers/viewer/phoneNumber/list.handler.ts
**/*Repository.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Repository files must include
Repositorysuffix, prefix with technology if applicable (e.g.,PrismaAppRepository.ts), and use PascalCase matching the exported class
Files:
packages/lib/server/repository/PrismaAgentRepository.tspackages/lib/server/repository/PrismaPhoneNumberRepository.ts
🧠 Learnings (9)
📓 Common learnings
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.
📚 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/updateInboundAgentEventType.handler.ts
📚 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/updateInboundAgentEventType.handler.tspackages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.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/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.tspackages/lib/server/repository/PrismaAgentRepository.tspackages/features/calAIPhone/providers/retellAI/types.ts
📚 Learning: 2025-08-26T20:23:28.396Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/features/calAIPhone/providers/retellAI/services/AgentService.ts:83-88
Timestamp: 2025-08-26T20:23:28.396Z
Learning: In calcom/cal.com PR #22995, the workflow update handler in packages/trpc/server/routers/viewer/workflows/update.handler.ts includes workflow-level authorization via isAuthorized(userWorkflow, ctx.user.id, "workflow.update") which validates the user can update the workflow before calling updateToolsFromAgentId (per maintainer Udit-takkar).
Applied to files:
packages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.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/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.tspackages/features/calAIPhone/providers/retellAI/types.tspackages/features/calAIPhone/providers/retellAI/RetellAIService.test.ts
📚 Learning: 2025-09-09T03:29:43.025Z
Learnt from: emrysal
PR: calcom/cal.com#23692
File: packages/lib/server/service/InsightsBookingBaseService.ts:16-16
Timestamp: 2025-09-09T03:29:43.025Z
Learning: In the Cal.com codebase, readonlyPrisma is still an instance of PrismaClient, making type changes from `typeof readonlyPrisma` to `PrismaClient` less critical since they are fundamentally compatible types.
Applied to files:
packages/lib/server/repository/PrismaAgentRepository.tspackages/lib/server/repository/PrismaPhoneNumberRepository.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/features/calAIPhone/providers/retellAI/types.ts
📚 Learning: 2025-08-21T16:34:10.839Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/phoneNumber/delete.handler.ts:13-24
Timestamp: 2025-08-21T16:34:10.839Z
Learning: In calcom/cal.com PR #22995, the deletePhoneNumber function in packages/trpc/server/routers/viewer/phoneNumber/delete.handler.ts is only used for imported phone numbers that don't have active Stripe subscriptions. Purchased phone numbers with subscriptions use a separate cancellation flow first (per maintainer Udit-takkar).
Applied to files:
packages/lib/server/repository/PrismaPhoneNumberRepository.tspackages/trpc/server/routers/viewer/phoneNumber/list.handler.ts
🧬 Code graph analysis (12)
packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.ts (2)
packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
PrismaPhoneNumberRepository(26-589)packages/features/calAIPhone/providers/interfaces/PhoneNumberRepositoryInterface.ts (1)
PhoneNumberData(90-103)
packages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.ts (3)
packages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.schema.ts (1)
TUpdateInboundAgentEventTypeInputSchema(9-9)packages/features/ee/workflows/components/agent-configuration/utils/promptUtils.ts (1)
replaceEventTypePlaceholders(28-40)packages/lib/server/repository/PrismaAgentRepository.ts (1)
PrismaAgentRepository(31-619)
packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts (1)
packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
PrismaPhoneNumberRepository(26-589)
packages/features/ee/billing/api/webhook/_checkout.session.completed.ts (2)
packages/lib/server/repository/PrismaAgentRepository.ts (1)
PrismaAgentRepository(31-619)packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
PrismaPhoneNumberRepository(26-589)
packages/lib/server/repository/PrismaAgentRepository.ts (1)
packages/prisma/index.ts (1)
PrismaClient(84-84)
packages/features/calAIPhone/providers/retellAI/types.ts (1)
packages/lib/server/repository/PrismaAgentRepository.ts (1)
PrismaAgentRepository(31-619)
packages/features/calAIPhone/providers/adapters/PrismaAgentRepositoryAdapter.ts (2)
packages/lib/server/repository/PrismaAgentRepository.ts (1)
PrismaAgentRepository(31-619)packages/features/calAIPhone/providers/interfaces/AgentRepositoryInterface.ts (3)
AgentData(82-92)AgentWithDetailsData(97-115)AgentWithPhoneNumbersData(120-124)
packages/features/ee/billing/api/webhook/_customer.subscription.deleted.ts (1)
packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
PrismaPhoneNumberRepository(26-589)
packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts (1)
packages/lib/server/repository/PrismaAgentRepository.ts (1)
PrismaAgentRepository(31-619)
apps/web/app/api/webhooks/retell-ai/route.ts (2)
packages/lib/server/repository/PrismaAgentRepository.ts (1)
PrismaAgentRepository(31-619)packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
PrismaPhoneNumberRepository(26-589)
packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
packages/prisma/index.ts (1)
PrismaClient(84-84)
packages/trpc/server/routers/viewer/phoneNumber/list.handler.ts (1)
packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
PrismaPhoneNumberRepository(26-589)
⏰ 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). (7)
- GitHub Check: Production builds / Build Web App
- GitHub Check: Production builds / Build Atoms
- GitHub Check: Production builds / Build API v2
- GitHub Check: Production builds / Build API v1
- GitHub Check: Tests / Unit
- GitHub Check: Linters / lint
- GitHub Check: Type check / check-types
🔇 Additional comments (34)
packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts (1)
1-20: Migration to instance-based repository pattern looks correct.The change correctly instantiates
PrismaPhoneNumberRepositorywith the Prisma client and calls the instance methodfindByStripeSubscriptionId. The pattern aligns with the repository class definition and maintains the existing error handling logic.packages/features/calAIPhone/providers/retellAI/RetellAIService.test.ts (5)
83-83: LGTM! Mock updated to reflect outbound agent API.The mock repository and agent repository methods have been correctly updated to use
createOutboundAgentandlinkOutboundAgentToWorkflow, aligning with the inbound/outbound agent split introduced in this PR.Also applies to: 107-107
171-171: LGTM! Test mocks updated for outbound agent creation.The test correctly mocks
createOutboundAgentinstead of the oldcreateAgentmethod, aligning with the new API surface.Also applies to: 194-194
545-545: LGTM! Test expectation updated toundefined.The test now expects
general_tools: undefinedinstead ofnull, reflecting the updated contract of theupdateLLMConfigurationmethod.
711-711: LGTM! Parameter renamed to support both agent types.The test expectation correctly uses
disconnectAgents: trueinstead ofdisconnectOutboundAgent, reflecting the generalized API that handles both inbound and outbound agents.
983-983: LGTM! Error handling test unchanged.The mock correctly returns
nullto test the error path when an agent is not found. No substantive changes detected.packages/features/ee/billing/api/webhook/_customer.subscription.deleted.ts (1)
16-20: LGTM: Instance-based repository pattern correctly applied.The switch from static
PrismaPhoneNumberRepository.findByStripeSubscriptionIdto instance-basedphoneNumberRepo.findByStripeSubscriptionId(...)aligns with the PR-wide migration to dependency-injected Prisma repositories.packages/trpc/server/routers/viewer/aiVoiceAgent/updateInboundAgentEventType.handler.ts (2)
50-55: LGTM: teamId now included in updateAgentConfiguration.Correctly passes
teamIdtoupdateAgentConfiguration, ensuring proper admin-access scoping for team-owned agents. This addresses the issue flagged in past reviews.
57-61: LGTM: Repository instantiation follows PR pattern.Instance-based
PrismaAgentRepository(prisma)usage aligns with the project-wide migration to dependency-injected repositories.packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts (1)
30-34: LGTM: Instance-based repository pattern correctly applied.Properly instantiates
PrismaAgentRepository(prisma)and calls the instance methodfindByIdWithCallAccess. This aligns with the PR-wide repository refactor.packages/trpc/server/routers/viewer/phoneNumber/list.handler.ts (1)
15-20: LGTM: Instance-based repository pattern correctly applied.Instantiates
PrismaPhoneNumberRepository(prisma)and calls the instance methodfindManyWithUserAccess. This aligns with the PR-wide migration to dependency-injected Prisma repositories.packages/features/ee/billing/api/webhook/_checkout.session.completed.ts (2)
103-108: LGTM: Instance-based repository pattern correctly applied.Properly instantiates
PrismaAgentRepository(prisma)and calls the instance methodfindByIdWithUserAccess.
134-144: LGTM: Instance-based repository pattern correctly applied.Correctly instantiates
PrismaPhoneNumberRepository(prisma)and callscreatePhoneNumber.packages/features/calAIPhone/providers/retellAI/types.ts (3)
105-107: LGTM: Type reference updated for instance-based repository.The change from
typeof PrismaAgentRepository.findByIdWithUserAccessAndDetailsto indexed accessPrismaAgentRepository["findByIdWithUserAccessAndDetails"]correctly reflects the migration from static methods to instance methods.
116-116: LGTM: inboundEventTypeId added to support inbound agent feature.This field enables linking inbound agents to event types, aligning with the PR's inbound call functionality.
171-171: LGTM: Method renamed to clarify outbound agent creation.Renaming
createAgenttocreateOutboundAgentaligns with the PR's outbound/inbound agent separation.packages/features/calAIPhone/providers/adapters/PrismaAgentRepositoryAdapter.ts (1)
83-91: LGTM: Outbound and inbound agent linkage methods correctly implemented.The split between
linkOutboundAgentToWorkflowandlinkInboundAgentToWorkflowproperly separates the two flows and addresses the past review feedback about clarifying the outbound agent linkage.packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.ts (1)
91-97: LGTM: Atomic inbound agent assignment method added.The
setInboundProviderAgentIdIfUnsetmethod properly delegates to the repository's atomic update logic, which prevents race conditions when assigning inbound agents to phone numbers.packages/lib/server/repository/PrismaAgentRepository.ts (3)
31-32: LGTM: Constructor-based dependency injection implemented.The migration from static methods to instance methods with
constructor(private prismaClient: PrismaClient)enables proper dependency injection and testing.
103-103: LGTM: inboundEventTypeId consistently added across all queries.The
inboundEventTypeIdfield is properly included in all relevant SELECT queries and result mappings, supporting the inbound agent feature.Also applies to: 142-142, 164-164, 183-183, 248-248, 308-308, 368-368, 410-410, 495-495, 526-526
560-588: LGTM: Outbound and inbound workflow linkage methods properly separated.The split between
linkOutboundAgentToWorkflow(setsagentId) andlinkInboundAgentToWorkflow(setsinboundAgentId) correctly supports the dual agent model.packages/lib/server/repository/PrismaPhoneNumberRepository.ts (13)
1-27: LGTM! Clean dependency injection pattern.The constructor correctly accepts a
PrismaClientinstance, enabling testability and loose coupling. Interface definitions are clear and appropriately typed.
29-41: LGTM!The method correctly queries user memberships and returns accessible team IDs. The migration to instance method is consistent with the new DI pattern.
43-64: LGTM!The method correctly queries by phone number and user ID. The use of
findFirstOrThrowprovides clear error handling semantics.
66-140: LGTM!The raw SQL queries are well-structured and efficiently fetch phone numbers with their associated inbound/outbound agents. The mapping logic correctly categorizes agents by type and combines results.
142-188: LGTM!The method correctly creates a phone number record with all necessary fields. The selection is comprehensive and includes the new inbound/outbound agent fields.
190-196: LGTM!The delete operation is straightforward and correctly uses the instance method pattern.
198-214: LGTM!The method correctly queries by Stripe subscription ID. The selection excludes agent fields, which appears intentional for billing-focused operations.
216-237: LGTM!The method correctly queries by ID and user ID with comprehensive field selection including agent references.
239-274: LGTM!The method correctly enforces team access control before querying. The null return for unauthorized access is appropriate.
276-311: LGTM!The method correctly enforces team access control and queries by phone number and team ID.
313-434: LGTM!The scope-based filtering logic is well-structured and correctly handles personal, team, and combined access patterns. The dynamic SQL construction uses Prisma's safe parameterization.
466-523: LGTM!The method correctly handles updating both inbound and outbound agents, with proper lookup and connect/disconnect logic for each.
574-588: LGTM! Enhanced context selection.The method correctly queries by phone number and returns enriched context including user and team details via nested selects.
|
@volnei Done |
packages/trpc/server/routers/viewer/aiVoiceAgent/setupInboundAgent.handler.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
packages/features/calAIPhone/providers/retellAI/services/AgentService.ts (3)
565-570: Check the correct field for existing inbound agent.The code checks
phoneNumberRecord.inboundAgentId, but the record stores the local agent ID. To verify whether an inbound agent is already configured on the Retell side, you should checkphoneNumberRecord.inboundProviderAgentIdinstead.Apply this diff:
- if (phoneNumberRecord.inboundAgentId) { + if (phoneNumberRecord.inboundProviderAgentId) { throw new HttpError({ statusCode: 400, message: "Inbound agent already configured for this phone number", }); }
572-614: Add error handling with compensating rollback.The inbound agent creation sequence (create agent → link workflow → update Retell phone → update local phone record) lacks error handling. If any step fails mid-flow, you'll have inconsistent state between Retell and the local database.
Wrap the flow in try/catch and implement compensating rollback actions: on error, attempt to revert the Retell phone update (clear inbound_agent_id), unlink the workflow-to-agent association, delete the newly created local agent record, and log rollback failures separately while re-throwing the original error.
655-658: Guard against undefined generalPrompt.The code can call
replaceEventTypePlaceholderswith an undefinedgeneralPromptif the caller omits it, causing a runtime error. Add a check to ensuregeneralPromptis defined before calling the function.Apply this diff:
const updatedPrompt = - agent.inboundEventTypeId && generalPrompt + agent.inboundEventTypeId && generalPrompt != null ? replaceEventTypePlaceholders(generalPrompt, agent.inboundEventTypeId) : generalPrompt;
🧹 Nitpick comments (1)
packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.ts (1)
19-20: Consider caching the repository instance.Every adapter method creates a new
PrismaPhoneNumberRepository(prisma)instance. Sinceprismais a singleton, consider creating the repository instance once in the adapter's constructor and reusing it.Apply this diff:
export class PrismaPhoneNumberRepositoryAdapter implements PhoneNumberRepositoryInterface { + private readonly repository: PrismaPhoneNumberRepository; + + constructor() { + this.repository = new PrismaPhoneNumberRepository(prisma); + } + async findByPhoneNumberAndUserId(params: { phoneNumber: string; userId: number; }): Promise<PhoneNumberData | null> { - const phoneNumberRepo = new PrismaPhoneNumberRepository(prisma); - return await phoneNumberRepo.findByPhoneNumberAndUserId(params); + return await this.repository.findByPhoneNumberAndUserId(params); } // ... apply similar changes to all other methodsAlso applies to: 28-29, 33-34, 42-43, 53-61, 65-66, 74-75, 83-88, 92-93, 99-100
📜 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.
📒 Files selected for processing (5)
packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.ts(3 hunks)packages/features/calAIPhone/providers/interfaces/PhoneNumberRepositoryInterface.ts(2 hunks)packages/features/calAIPhone/providers/retellAI/services/AgentService.ts(22 hunks)packages/lib/server/repository/PrismaPhoneNumberRepository.ts(19 hunks)packages/trpc/server/routers/viewer/aiVoiceAgent/setupInboundAgent.handler.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/trpc/server/routers/viewer/aiVoiceAgent/setupInboundAgent.handler.ts
🧰 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 useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/features/calAIPhone/providers/interfaces/PhoneNumberRepositoryInterface.tspackages/features/calAIPhone/providers/retellAI/services/AgentService.tspackages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.tspackages/lib/server/repository/PrismaPhoneNumberRepository.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/interfaces/PhoneNumberRepositoryInterface.tspackages/features/calAIPhone/providers/retellAI/services/AgentService.tspackages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.tspackages/lib/server/repository/PrismaPhoneNumberRepository.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/features/calAIPhone/providers/interfaces/PhoneNumberRepositoryInterface.tspackages/features/calAIPhone/providers/retellAI/services/AgentService.tspackages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.tspackages/lib/server/repository/PrismaPhoneNumberRepository.ts
**/*Service.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Service files must include
Servicesuffix, use PascalCase matching exported class, and avoid generic names (e.g.,MembershipService.ts)
Files:
packages/features/calAIPhone/providers/retellAI/services/AgentService.ts
**/*Repository.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Repository files must include
Repositorysuffix, prefix with technology if applicable (e.g.,PrismaAppRepository.ts), and use PascalCase matching the exported class
Files:
packages/lib/server/repository/PrismaPhoneNumberRepository.ts
🧠 Learnings (7)
📚 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/interfaces/PhoneNumberRepositoryInterface.tspackages/features/calAIPhone/providers/retellAI/services/AgentService.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/AgentService.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/services/AgentService.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/services/AgentService.ts
📚 Learning: 2025-08-14T23:07:45.282Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/features/calAIPhone/providers/retellAI/services/PhoneNumberService.ts:312-319
Timestamp: 2025-08-14T23:07:45.282Z
Learning: In calcom/cal.com phone number import flow, an agent must always be present when importing a phone number. The validateAgentPermissions function in PhoneNumberService should throw a 400 error when agentId is missing, as this is a business requirement rather than optional functionality (per maintainer Udit-takkar).
Applied to files:
packages/features/calAIPhone/providers/retellAI/services/AgentService.ts
📚 Learning: 2025-09-09T03:29:43.025Z
Learnt from: emrysal
PR: calcom/cal.com#23692
File: packages/lib/server/service/InsightsBookingBaseService.ts:16-16
Timestamp: 2025-09-09T03:29:43.025Z
Learning: In the Cal.com codebase, readonlyPrisma is still an instance of PrismaClient, making type changes from `typeof readonlyPrisma` to `PrismaClient` less critical since they are fundamentally compatible types.
Applied to files:
packages/lib/server/repository/PrismaPhoneNumberRepository.ts
📚 Learning: 2025-08-21T16:34:10.839Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/phoneNumber/delete.handler.ts:13-24
Timestamp: 2025-08-21T16:34:10.839Z
Learning: In calcom/cal.com PR #22995, the deletePhoneNumber function in packages/trpc/server/routers/viewer/phoneNumber/delete.handler.ts is only used for imported phone numbers that don't have active Stripe subscriptions. Purchased phone numbers with subscriptions use a separate cancellation flow first (per maintainer Udit-takkar).
Applied to files:
packages/lib/server/repository/PrismaPhoneNumberRepository.ts
🧬 Code graph analysis (3)
packages/features/calAIPhone/providers/retellAI/services/AgentService.ts (5)
packages/features/calAIPhone/providers/retellAI/types.ts (1)
RetellAIRepository(163-194)packages/features/calAIPhone/providers/interfaces/AgentRepositoryInterface.ts (1)
AgentRepositoryInterface(6-77)packages/features/calAIPhone/providers/interfaces/PhoneNumberRepositoryInterface.ts (1)
PhoneNumberRepositoryInterface(8-87)packages/features/ee/workflows/components/agent-configuration/utils/promptUtils.ts (1)
replaceEventTypePlaceholders(28-40)packages/features/calAIPhone/providers/retellAI/RetellAIServiceMapper.ts (1)
RetellAIServiceMapper(24-210)
packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.ts (2)
packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
PrismaPhoneNumberRepository(26-564)packages/features/calAIPhone/providers/interfaces/PhoneNumberRepositoryInterface.ts (1)
PhoneNumberData(92-105)
packages/lib/server/repository/PrismaPhoneNumberRepository.ts (1)
packages/prisma/index.ts (1)
PrismaClient(84-84)
⏰ 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
🔇 Additional comments (10)
packages/features/calAIPhone/providers/retellAI/services/AgentService.ts (1)
24-32: LGTM!The Dependencies type and constructor refactor improves maintainability by consolidating all dependencies into a single object.
Based on learnings
packages/features/calAIPhone/providers/interfaces/PhoneNumberRepositoryInterface.ts (2)
66-66: LGTM!Renaming
disconnectOutboundAgenttodisconnectAgentsaccurately reflects that both inbound and outbound agents are now disconnected.
78-86: LGTM!The new methods
updateInboundAgentIdandfindInboundAgentIdByPhoneNumberIdsupport atomic inbound agent setup and race condition detection.packages/features/calAIPhone/providers/adapters/PrismaPhoneNumberRepositoryAdapter.ts (2)
91-94: LGTM!The new
updateInboundAgentIdmethod correctly delegates to the repository's atomic update operation.
96-101: LGTM!The new
findInboundAgentIdByPhoneNumberIdmethod correctly delegates to the repository for race condition detection.packages/lib/server/repository/PrismaPhoneNumberRepository.ts (5)
27-27: LGTM!Converting to an instance-based repository with constructor injection improves testability and allows for different PrismaClient instances (e.g., for testing or multi-tenant scenarios).
436-464: LGTM!The renamed
disconnectAgentsparameter and dual-disconnect logic (both inbound and outbound agents) align with the PR's intent to support inbound calls.
525-540: LGTM!The atomic
updateInboundAgentIdusingupdateManywith a NULL check correctly prevents race conditions. The caller can checkcount === 0to detect concurrent setup attempts.
542-547: LGTM!The
findInboundAgentIdByPhoneNumberIdmethod supports race condition detection by allowing the caller to verify which agent was actually configured.
549-563: LGTM!Enriching
findByPhoneNumberwith user and team information supports better context in logs or error messages when handling phone number operations.
...es/features/ee/workflows/components/agent-configuration/components/tabs/IncomingCallsTab.tsx
Show resolved
Hide resolved
* feat: lang support * fix: type errors * feat: select voice agent * refactor: address feedback * refactor: address feedback * refactor: missing import * fix: types * feat: add inbound calls * chore: formatting * chore * feat: finish inbound call * chore: formatting * fix: update bug * fix: types * refactor: Agent Configuration Sheet (#23930) * refactor: agent configuration sheet * chore: use default phone numbre * refactor: improvements * refactor: improvements * fix: types * fix: feedback * chore: * fix: feedback * fix: prompt * fix: review * fix: review * refactor: class * refactor: class * refactor: rename * Update apps/web/public/static/locales/en/common.json * Update apps/web/public/static/locales/en/common.json * chore: update set value * fix: remove index * fix: type error * fix: update tetss * fix: use logger * refactor: don't use static * fix: type * fix: schema * refactor: --------- Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
What does this PR do?
This PR adds support for inbound calls where users can call the phone number directly.
This PR adds support for inbound calls for cal ai. To be reviewed after #23890 is merged in this branch
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?