Files
calendar/packages/features/tasker
Udit TakkarGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Udit TakkarUdit Takkar
4081d11fbe feat: workflow auto translation (#27087)
* feat: workflow auto translation

* tests: add unit tests

* refactor: tests and workflow

* fix: type err

* fix: type err

* fix: remove redundant index on WorkflowStepTranslation

The @@index on [workflowStepId, field, targetLocale] duplicates the @@unique
constraint on the same columns. A unique index already provides efficient
lookups, so the separate @@index adds storage overhead and write latency
without benefit.

Addresses Cubic AI review feedback (confidence 9/10).

Co-Authored-By: unknown <>

* fix: correct locale mapping when translation API returns null

Map translations with their corresponding locales before filtering to
preserve correct locale-to-translation associations. Previously, filtering
out null translations would reindex the array, causing incorrect locale
mappings when any translation in the batch failed.

Also fixes pre-existing lint warnings:
- Move exports to end of file
- Add explicit return type to processTranslations
- Replace ternary with if-else for upsertMany selection

Co-Authored-By: udit@cal.com <udit222001@gmail.com>

* fix: address review feedback for workflow auto-translation

- Add change detection before creating translation tasks
- Rename userLocale to sourceLocale in task props for clarity
- Show source language in UI with new translation key
- Extract SUPPORTED_LOCALES to shared translationConstants.ts
- Fix locale mapping bug in translateEventTypeData.ts
- Add WhatsApp translation support
- Abstract translation lookup into shared translationLookup.ts helper
- Restore if-else readability for SCANNING_WORKFLOW_STEPS

Co-authored-by: Udit Takkar <udit.takkar@cal.com>
Co-Authored-By: unknown <>

* fix: update test to use sourceLocale instead of userLocale

Co-Authored-By: unknown <>

* refactor: feedback

* fix: handle first time

* fix: tests

* fix: tests

* fix: address Cubic AI review feedback (confidence 9/10 issues)

- WhatsApp translation: Apply variable substitution using getSMSMessageWithVariables
  and clear contentSid when using translated body to ensure Twilio uses the
  translated text instead of the original template

- update.handler.ts: Change sourceLocale assignment from ?? to || for consistency
  with tasker payload behavior (line 481)

- ITranslationService.ts: Rename methods from plural to singular naming:
  - getWorkflowStepTranslations -> getWorkflowStepTranslation
  - getEventTypeTranslations -> getEventTypeTranslation
  Updated all call sites and tests accordingly

Co-Authored-By: unknown <>

* fix: address Cubic AI review feedback (confidence 9/10+ issues)

- Fix getSMSMessageWithVariables to handle WHATSAPP_ATTENDEE action for
  locale and timezone (confidence 9/10)
- Remove WhatsApp translation feature that set contentSid to undefined
  since Twilio ignores body parameter for WhatsApp and requires
  pre-approved Message Templates (confidence 10/10)

Co-Authored-By: unknown <>

* fix: translatio

* Add tests: packages/features/eventTypeTranslation/repositories/EventTypeTranslationRepository.test.ts

Generated by Paragon from proposal for PR #27087

* Add tests: packages/features/tasker/tasks/translateWorkflowStepData.test.ts

Generated by Paragon from proposal for PR #27087

* chore: nit

* chore: verfied atg

* fix: set sourceLocale for new steps, add shouldDirty to checkbox, remove spec docs

- Set sourceLocale fallback in addedSteps mapping to fix stale detection mismatch
- Add { shouldDirty: true } to autoTranslateEnabled checkbox onChange
- Remove specs/workflow-translation/ directory (planning docs, not for repo)

Co-authored-by: Udit Takkar <udit.07.takkar@gmail.com>
Co-Authored-By: unknown <>

* chore: add specs back

* fix: type error

* fix: type error

* fix: type err

* fix: tests

* refactor: feedback

* fix: type err

* refactor

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.takkar@cal.com>
Co-authored-by: Udit Takkar <udit.07.takkar@gmail.com>
2026-02-25 01:03:55 +05:30
..

Tasker

Tasker: "One who performs a task, as a day-laborer."

Task: "A function to be performed; an objective."

What is it?

Introduces a new pattern called Tasker which may be switched out in the future for other third party services.

Also introduces a base InternalTasker which doesn't require third party dependencies and should work out of the box (by configuring a proper cron).

Why is this needed?

The Tasker pattern is needed to streamline the execution of non-critical tasks in an application, providing a structured approach to task scheduling, execution, retrying, and cancellation. Here's why it's necessary:

  1. Offloading non-critical tasks: There are tasks that don't need to be executed immediately on the main thread, such as sending emails, generating reports, or performing periodic maintenance tasks. Offloading these tasks to a separate queue or thread improves the responsiveness and efficiency of the main application.

  2. Retry mechanism: Not all tasks succeed on the first attempt due to errors or external dependencies. This pattern incorporates a retry mechanism, which allows failed tasks to be retried automatically for a specified number of attempts. This improves the robustness of the system by handling temporary failures gracefully.

  3. Scheduled task execution: Some tasks need to be executed at a specific time or after a certain delay. The Tasker pattern facilitates scheduling tasks for future execution, ensuring they are performed at the designated time without manual intervention.

  4. Task cancellation: Occasionally, it's necessary to cancel a scheduled task due to changing requirements or user actions. The Tasker pattern supports task cancellation, enabling previously scheduled tasks to be revoked or removed from the queue before execution.

  5. Flexible implementation: The Tasker pattern allows for flexibility in implementation by providing a base structure (InternalTasker) that can be extended or replaced with third-party services (TriggerDevTasker, AwsSqsTasker, etc.). This modularity ensures that the task execution mechanism can be adapted to suit different application requirements or environments.

Overall, the Tasker pattern enhances the reliability, performance, and maintainability by managing non-critical tasks in a systematic and efficient manner. It abstracts away the complexities of task execution, allowing developers to focus on core application logic while ensuring timely and reliable execution of background tasks.

How does it work?

Since the Tasker is a pattern on itself, it will depend on the actual implementation. For example, a TriggerDevTasker will work very differently from an AwsSqsTasker.

For simplicity sake will explain how the InternalTasker works:

  • Instead of running a non-critical task you schedule using the tasker:

    const examplePayload = { example: "payload" };
    - await sendWebhook(examplePayload);
    + await tasker.create("sendWebhook", JSON.stringify(examplePayload));
    
  • This will create a new task to be run on the next processing of the task queue.

  • Then on the next cron run it will be picked up and executed:

    // /app/api/tasks/cron/route.ts
    import { TaskProcessor } from "@calcom/features/tasker/task-processor";
    
    export async function GET() {
      // authenticate the call...
      const processor = new TaskProcessor();
      await processor.processQueue();
      return Response.json({ success: true });
    }
    
  • By default, the cron will run each minute and will pick the next 100 tasks to be executed.

  • If the tasks succeeds, it will be marked as suceededAt: new Date(). If if fails, the attempts prop will increase by 1 and will be retried on the next cron run.

  • If attempts reaches maxAttemps, it will be considered a failed and won't be retried again.

  • By default, tasks will be attempted up to 3 times. This can be overridden when creating a task.

  • From here we can either keep a record of executed tasks, or we can setup another cron to cleanup all successful and failed tasks:

    // /app/api/tasks/cleanup/route.ts
    import { TaskProcessor } from "@calcom/features/tasker/task-processor";
    
    export async function GET() {
      // authenticate the call...
      const processor = new TaskProcessor();
      await processor.cleanup();
      return Response.json({ success: true });
    }
    
  • This will delete all failed and successful tasks.

  • A task is just a simple function receives a payload:

    type TaskHandler = (payload: string) => Promise<void>;
    

How to contribute?

You can contribute by either expanding the InternalTasker or creating new Taskers. To see how to add new Taskers, see the tasker-factory.ts file.

You can also take some inspiration by looking into previous attempts to add various Message Queue pull requests: