fix(citas): agendar funciona end-to-end — Cal.com v2 + fecha real en el prompt - #5
fix(citas): agendar funciona end-to-end — Cal.com v2 + fecha real en el prompt#5mfonseca10 wants to merge 1 commit into
Conversation
… servidor y fecha real en el prompt - scheduleAppointment usa el cliente v2 de integrations/calcom.ts en vez de la llamada v1 incompleta: eventTypeId y timezone resueltos en el servidor (el modelo los alucinaba), modo de consulta de horarios libres, guardia date_in_past y logging de fallos para wrangler tail - el system prompt inyecta la fecha/hora actual del negocio (currentDateLine): sin ella el LLM propone fechas de su entrenamiento (pedía slots de 2023) - 6 pruebas del conector actualizadas/nuevas Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughChangesScheduling and date context
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant scheduleAppointmentTool
participant CalcomIntegration
participant CalComAPI
scheduleAppointmentTool->>CalcomIntegration: Resolve timezone and event type
scheduleAppointmentTool->>CalcomIntegration: Query slots or create booking
CalcomIntegration->>CalComAPI: Send availability or booking request
CalComAPI-->>CalcomIntegration: Return slots, booking, or HTTP failure
CalcomIntegration-->>scheduleAppointmentTool: Return result or structured error
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
test/tools/scheduleAppointment.test.ts (1)
9-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace the hard-coded
2027-06-01fixture with a relative future date.Five tests in this file hard-code
startTime/datevalues around2027-06-01. The tool under test rejects any date earlier than "today" (Line 43 insrc/tools/scheduleAppointment.ts). Given the current date,2027-06-01is less than a year away; once real time passes it, these tests will start failing the past-date guard rather than testing the intended behavior.Compute a fixture date relative to
new Date()(for example, one year out) instead of a fixed literal, so the suite stays valid regardless of when it runs.+const futureDate = new Date(); +futureDate.setFullYear(futureDate.getFullYear() + 1); +const futureDateStr = futureDate.toISOString().slice(0, 10); +const futureStartTime = `${futureDateStr}T17:00:00-06:00`;Then use
futureStartTime/futureDateStrin place of the2027-06-01literals across the affected tests.Also applies to: 35-48, 50-63, 77-94
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/tools/scheduleAppointment.test.ts` around lines 9 - 33, Replace the fixed 2027-06-01 date literals in the affected tests with shared future fixtures computed from new Date(), such as futureStartTime and futureDateStr representing a date about one year ahead. Update the startTime/date inputs across the listed test cases while preserving their existing assertions and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/integrations/calcom.ts`:
- Around line 91-96: Update the Cal.com response logging in the slots request
flow to avoid raw response bodies: replace the !res.ok logging with HTTP status
plus a truncated non-PII error field such as error.code or error.message, and
change the success log to report only a concise slot-count summary instead of
JSON.stringify(body.data). Preserve the existing return behavior and locate
these changes around the slots response handling and createBooking-related error
paths.
In `@src/system-prompt.ts`:
- Line 191: Update the system prompt timezone assignment near today to reuse
calcomTimeZone(env) instead of duplicating the CALCOM_TIMEZONE trim/default
expression, and import calcomTimeZone from ../integrations/calcom. Keep the
existing currentDateLine behavior unchanged by passing the helper’s result to
it.
In `@src/tools/scheduleAppointment.ts`:
- Around line 25-26: Enforce strict zero-padded YYYY-MM-DD validation in the
scheduleAppointment schema’s date field, and validate the date portion extracted
from startTime before the past-date guard compares it with today. Reject
malformed or unpadded values before reaching the requestedDay < today
comparison, while preserving valid date and ISO datetime handling.
---
Nitpick comments:
In `@test/tools/scheduleAppointment.test.ts`:
- Around line 9-33: Replace the fixed 2027-06-01 date literals in the affected
tests with shared future fixtures computed from new Date(), such as
futureStartTime and futureDateStr representing a date about one year ahead.
Update the startTime/date inputs across the listed test cases while preserving
their existing assertions and behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 0fc8fd65-e0d7-415c-8731-dee047168c8d
📒 Files selected for processing (4)
src/integrations/calcom.tssrc/system-prompt.tssrc/tools/scheduleAppointment.tstest/tools/scheduleAppointment.test.ts
| if (!res.ok) { | ||
| console.warn(`[calcom] slots http_${res.status}:`, (await res.text()).slice(0, 300)); | ||
| return { ok: false, reason: `http_${res.status}` }; | ||
| } | ||
| const body = (await res.json()) as { data?: Record<string, Slot[]> }; | ||
| console.log(`[calcom] slots ok eventType=${eventTypeId} date=${date} tz=${timeZone}:`, JSON.stringify(body.data ?? {}).slice(0, 300)); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does the Cal.com API v2 /v2/bookings error response ever include the submitted attendee email or name in the error message?
💡 Result:
The Cal.com API v2 error response format does not explicitly include a dedicated field for the submitted attendee email [1][2][3]. According to the official documentation, Cal.com API v2 uses a consistent, structured error response that contains a status, code, and message [1][2][3]. The structure is as follows: { "status": "error", "error": { "code": "SOME_ERROR_CODE", "message": "A description of the error" } } While the error message string (in the "message" field) could technically contain dynamic content depending on the specific validation failure—such as missing required fields or invalid input—there is no systematic inclusion of the submitted attendee email in the standard error response schema [1][4][3]. In practice, error messages are typically generic descriptions of the failed validation or server-side issue [1][5][3].
Citations:
- 1: https://cal.com/docs/api-reference/v2/v1-v2-differences
- 2: https://calcom.mintlify.dev/docs/api-reference/v2/v1-v2-differences
- 3: https://mintlify.wiki/calcom/cal.com/api/introduction
- 4: When creating a booking via the
/v2/bookingsendpoint, the API returns a400 BAD_REQUESTerror with the message: responses - {title}error_required_field calcom/cal.diy#24851 - 5: API create booking request - 500 Internal Server Error calcom/cal.diy#23529
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file size =="
wc -l src/integrations/calcom.ts 2>/dev/null || true
echo "== relevant calcom.ts sections =="
sed -n '1,180p' src/integrations/calcom.ts
echo "== requests for calcom logging =="
rg -n "Cal.com|calcom|/v2/bookings|createBooking|getAvailableSlots|console\.log|console\.warn|HTTP_|http_|res\.text|res\.json|bookings" -S .Repository: santmun/forja
Length of output: 37450
🌐 Web query:
Cal.com API v2 bookings validation error email name validation message
💡 Result:
When using the Cal.com API v2 to create bookings (POST /v2/bookings), validation errors—such as those involving attendee information (name, email) or other required fields—are typically returned as 400 Bad Request responses containing a structured error object [1][2]. Key details regarding validation and error messages: 1. Structured Error Format: API v2 provides structured error responses [1]. If a validation fails, the response will generally have a status of "error" and include an error code (e.g., BAD_REQUEST) along with specific details about which fields failed validation [1][2][3]. 2. Attendee Validation: - Name: Recent updates to the API have improved name validation to be more flexible, for instance by making the last name optional while still requiring a valid, non-empty first name [4]. - Contact Methods: The API requires at least one valid contact method—either an email or a phone number [5][6]. If an email is provided, it must follow a valid email format [5][6]. 3. Required Fields: If an event type has mandatory booking fields defined in its configuration, failing to provide values for these in the bookingFieldsResponses object will trigger a validation error [7]. Recent efforts have been made to improve these error messages to explicitly name the missing fields rather than returning generic or 500-level errors [7][8]. 4. Troubleshooting Common Errors: - 400 Bad Request: Usually indicates malformed JSON, missing required fields (like attendee information or timeZone), or invalid data types (e.g., sending an integer as a string) [2][9][3]. - 500 Internal Server Error: While the API aims to return 400s for validation issues, some complex edge cases (like lack of availability or deep configuration issues) have historically resulted in 500 errors; these are actively being tracked and refactored for better clarity [2][10][8]. For the most accurate debugging, ensure your request includes the correct cal-api-version header (e.g., 2024-08-13) and check the "details" or "errors" array in the JSON response body for the specific field-level constraints [1][2][9][3].
Citations:
- 1: https://cal.com/docs/api-reference/v2/v1-v2-differences
- 2: fix: improve API v2 error handling for validation errors calcom/cal.diy#27662
- 3: Kept getting bad request exception when making curl calls calcom/cal.diy#23136
- 4: fix: skip last name check when creating a booking in v2 calcom/cal.diy#22794
- 5: https://github.com/calcom/cal.com/blob/089a39f5/packages/platform/types/bookings/2024-08-13/inputs/create-booking.input.ts
- 6: https://github.com/calcom/cal.com/blob/f7b2f276/packages/platform/types/bookings/2024-08-13/inputs/create-booking.input.ts
- 7: When creating a booking via the
/v2/bookingsendpoint, the API returns a400 BAD_REQUESTerror with the message: responses - {title}error_required_field calcom/cal.diy#24851 - 8: API create booking request - 500 Internal Server Error calcom/cal.diy#23529
- 9: Documentation "Try it" console send Integers as strings causing validation error calcom/cal.diy#27989
- 10: API Booking Creation 500 Error calcom/cal.diy#11948
Avoid logging raw Cal.com response bodies.
createBooking sends attendee name, email, and optional phoneNumber. Error logging uses (await res.text()), which can contain fields such as attendee data returned in Cal.com error messages. Log only the HTTP status and a truncated non-PII field such as error.code or error.message instead of the full raw body. The success log also outputs the entire /v2/slots payload; reduce it to a short summary such as the slot count.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/integrations/calcom.ts` around lines 91 - 96, Update the Cal.com response
logging in the slots request flow to avoid raw response bodies: replace the
!res.ok logging with HTTP status plus a truncated non-PII error field such as
error.code or error.message, and change the success log to report only a concise
slot-count summary instead of JSON.stringify(body.data). Preserve the existing
return behavior and locate these changes around the slots response handling and
createBooking-related error paths.
| tone: overrides?.tone, | ||
| extraEscalationKeywords: overrides?.extraEscalationKeywords, | ||
| lessons: overrides?.lessons, | ||
| today: currentDateLine((env.CALCOM_TIMEZONE || "").trim() || "America/Mexico_City"), |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify DEFAULT_TZ value and current imports in system-prompt.ts
set -euo pipefail
fd -e ts . src/integrations --exec cat -n {}
echo "---"
fd system-prompt.ts src --exec cat -n {}Repository: santmun/forja
Length of output: 15480
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Top-level files:"
fd -e ts -e toml -e json -e yaml -e yml . src . --max-depth 3
echo "--- imports containing calcom:"
rg -n 'from ["'\''](\./|../)\.?integrations/.*calcom|calcomTimeZone|DEFAULT_TZ|systemPromptFromEnv' src --glob '*.ts'Repository: santmun/forja
Length of output: 6750
Reuse calcomTimeZone(env) in the system prompt timezone fallback.
src/system-prompt.ts:191 duplicates calcomTimeZone(env) fallback logic, while scheduleAppointmentTool also uses calcomTimeZone(env). Import calcomTimeZone from ../integrations/calcom there so changes to the trim/default behavior apply in one place.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/system-prompt.ts` at line 191, Update the system prompt timezone
assignment near today to reuse calcomTimeZone(env) instead of duplicating the
CALCOM_TIMEZONE trim/default expression, and import calcomTimeZone from
../integrations/calcom. Keep the existing currentDateLine behavior unchanged by
passing the helper’s result to it.
| date: z.string().optional().describe("YYYY-MM-DD para consultar horarios libres de ese día"), | ||
| startTime: z.string().optional().describe("ISO datetime con offset para reservar, ej. 2026-08-03T15:00:00-06:00"), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Enforce YYYY-MM-DD format before the past-date comparison.
The past-date guard at Line 43 compares requestedDay < today as strings. This only works correctly if requestedDay is zero-padded (YYYY-MM-DD). today is always correctly padded because it comes from todayInTz, but date and startTime (Lines 25-26) have no format constraint in the schema. An unpadded date from the model (for example "2027-6-1") sorts lexicographically after a padded today even when it represents an earlier calendar date, silently defeating the guard this PR intentionally adds against "fecha fantasma."
Add a regex constraint to date (and validate the date portion of startTime) so malformed values are rejected before the comparison runs.
🐛 Proposed fix
inputSchema: z.object({
- date: z.string().optional().describe("YYYY-MM-DD para consultar horarios libres de ese día"),
- startTime: z.string().optional().describe("ISO datetime con offset para reservar, ej. 2026-08-03T15:00:00-06:00"),
+ date: z
+ .string()
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
+ .optional()
+ .describe("YYYY-MM-DD para consultar horarios libres de ese día"),
+ startTime: z
+ .string()
+ .regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)
+ .optional()
+ .describe("ISO datetime con offset para reservar, ej. 2026-08-03T15:00:00-06:00"),Also applies to: 41-49
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/tools/scheduleAppointment.ts` around lines 25 - 26, Enforce strict
zero-padded YYYY-MM-DD validation in the scheduleAppointment schema’s date
field, and validate the date portion extracted from startTime before the
past-date guard compares it with today. Reject malformed or unpadded values
before reaching the requestedDay < today comparison, while preserving valid date
and ISO datetime handling.
Qué cambia
Hace que agendar citas funcione de verdad: la tool
scheduleAppointmentahora usa el cliente Cal.com v2 que ya vive enintegrations/calcom.ts(antes llamaba a la API v1 con payload incompleto), eleventTypeIdy la zona horaria se resuelven en el servidor (el modelo ya no los inventa), se agrega modo de consulta de horarios libres (getAvailableSlots), y el system prompt ahora incluye la fecha/hora actual del negocio (currentDateLine) — sin eso, el LLM propone fechas de su época de entrenamiento. Guardia extradate_in_pasten la tool y logging de fallos de Cal.com para diagnóstico conwrangler tail.Por qué
Con un bot real en producción, el flujo de citas fallaba siempre, por tres capas:
api.cal.com/v1sintimeZone/language/metadata→ rechazo.eventTypeIdconfigurado → el modelo lo alucinaba.2023-10-04y Cal.com respondía vacío ("no hay horarios disponibles").Con este fix, el flujo completo quedó verificado end-to-end: el bot ofrece los horarios reales del calendario y crea la reserva (confirmación + invitación de calendario al cliente).
Cómo lo probaste
pnpm testpasa (440 pruebas, incluye 6 nuevas del tool: booking v2, slots, fecha pasada, sin API key, sin event type)pnpm typechecklimpioChecklist
member/(config de cada quien)Encontrado y arreglado desplegando un bot real de nuestra agencia (SynapMex 🇲🇽). Gracias por Forja — seguiremos regresando mejoras.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes