diff --git a/routes/task_routes.py b/routes/task_routes.py
index d38040fde..4d0496886 100644
--- a/routes/task_routes.py
+++ b/routes/task_routes.py
@@ -594,6 +594,7 @@ def setup_task_routes(task_scheduler) -> APIRouter:
cache_tables = {
"summarize_emails": ("email_summaries",),
"draft_email_replies": ("email_ai_replies",),
+ "email_auto_translate": ("email_translations",),
"extract_email_events": ("email_calendar_extractions",),
"learn_sender_signatures": ("sender_signatures",),
"check_email_urgency": ("email_tags", "email_urgency_alerts"),
diff --git a/src/builtin_actions.py b/src/builtin_actions.py
index 8d80ee7ff..7a807636d 100644
--- a/src/builtin_actions.py
+++ b/src/builtin_actions.py
@@ -569,6 +569,21 @@ async def action_draft_email_replies(owner: str, **kwargs) -> Tuple[str, bool]:
return str(e), False
+async def action_email_auto_translate(owner: str, **kwargs) -> Tuple[str, bool]:
+ """Placeholder for the opt-in email translation enrichment task.
+
+ The task is seeded paused so it is visible in Tasks while the detector,
+ cache, and on-demand translation path are built. Returning False makes a
+ manual run show a clear message instead of pretending work happened.
+ """
+ return (
+ "Email auto-translate is registered but not implemented yet. "
+ "Next step: detect foreign-language emails, cache translations by body hash, "
+ "and stream translated text in the email reader without replacing the original.",
+ False,
+ )
+
+
_TYPE_COLORS = {
"work": "#5b8abf", # blue
"personal": "#a07ae0", # purple
@@ -2487,6 +2502,7 @@ BUILTIN_ACTIONS = {
"tidy_research": action_tidy_research,
"summarize_emails": action_summarize_emails,
"draft_email_replies": action_draft_email_replies,
+ "email_auto_translate": action_email_auto_translate,
"extract_email_events": action_extract_email_events,
"classify_events": action_classify_events,
# ping_events removed from the user-facing registry. Calendar reminders
@@ -2511,6 +2527,7 @@ BUILTIN_ACTION_INFO = {
"tidy_research": "Remove orphaned research files (sessions that were deleted)",
"summarize_emails": "Pre-generate AI summaries for new inbox emails",
"draft_email_replies": "Pre-draft AI reply suggestions for new inbox emails",
+ "email_auto_translate": "Detect foreign-language emails and cache translated text for the email reader",
"extract_email_events": "Scan emails for booking/meeting confirmations and auto-add to calendar",
"classify_events": "Tag upcoming events with importance (low/normal/high/critical) and type (work/health/travel/etc.); colors them too",
"daily_brief": "Build a morning digest: today's calendar, unread email count + top senders, active todos",
diff --git a/src/task_scheduler.py b/src/task_scheduler.py
index afd0dc581..3b898deb3 100644
--- a/src/task_scheduler.py
+++ b/src/task_scheduler.py
@@ -239,6 +239,7 @@ HOUSEKEEPING_DEFAULTS = {
"tidy_research": {"name": "Research Tidy", "trigger_type": "event", "trigger_event": "research_completed", "trigger_count": 5, "schedule": None, "scheduled_time": None, "cron_expression": None, "legacy_names": ["Tidy Research"]},
"summarize_emails": {"name": "Email (Summary)", "schedule": "cron", "scheduled_time": None, "cron_expression": "0 */2 * * *", "ship_paused": True, "legacy_names": ["Tidy Email (Summary)"]},
"draft_email_replies": {"name": "Email AI Auto Reply", "schedule": "cron", "scheduled_time": None, "cron_expression": "0 */2 * * *", "ship_paused": True, "legacy_names": ["Tidy Email (Replies)", "AI Auto Reply"]},
+ "email_auto_translate": {"name": "Auto-translate Emails", "schedule": "cron", "scheduled_time": None, "cron_expression": "0 */2 * * *", "ship_paused": True, "legacy_names": ["Auto Translate Email", "Email Auto Translate"]},
"extract_email_events": {"name": "Email Calendar Events", "schedule": "cron", "scheduled_time": None, "cron_expression": "0 */1 * * *", "ship_paused": True, "legacy_names": ["Email → Calendar Events"]},
"classify_events": {"name": "Calendar Classify Events", "schedule": "cron", "scheduled_time": None, "cron_expression": "0 6,18 * * *", "ship_paused": True, "legacy_names": ["Classify Calendar Events"]},
"check_email_urgency": {"name": "Email Tags", "schedule": "cron", "scheduled_time": None, "cron_expression": "0 * * * *", "ship_paused": True, "old_cron_expressions": ["*/15 * * * *"], "legacy_names": ["Email Triage", "Urgent Email"]},
@@ -1014,6 +1015,7 @@ class TaskScheduler:
"learn_sender_signatures",
"summarize_emails",
"draft_email_replies",
+ "email_auto_translate",
"extract_email_events",
"classify_events",
"tidy_sessions",
@@ -1027,6 +1029,7 @@ class TaskScheduler:
_MODEL_BACKED_ACTIONS = frozenset({
"summarize_emails",
"draft_email_replies",
+ "email_auto_translate",
"extract_email_events",
"classify_events",
"learn_sender_signatures",
diff --git a/static/js/tasks.js b/static/js/tasks.js
index 4674be40c..6b4817440 100644
--- a/static/js/tasks.js
+++ b/static/js/tasks.js
@@ -215,6 +215,7 @@ async function _saveUrgentEmailSettings(prompt) {
const _EMAIL_ACCOUNT_ACTIONS = new Set([
'summarize_emails',
'draft_email_replies',
+ 'email_auto_translate',
'extract_email_events',
'check_email_urgency',
]);
@@ -391,6 +392,7 @@ const _TASK_ICONS = {
// Email
summarize_emails: '',
draft_email_replies: '',
+ email_auto_translate:'',
extract_email_events:'',
classify_events: '',
learn_sender_signatures:'',
@@ -418,6 +420,7 @@ function _taskIcon(task) {
const _MODEL_BACKED_ACTIONS = new Set([
'summarize_emails',
'draft_email_replies',
+ 'email_auto_translate',
'extract_email_events',
'classify_events',
'learn_sender_signatures',
@@ -562,6 +565,7 @@ const _CATEGORY_MAP = {
extract_email_events: 'Calendar',
summarize_emails: 'Email',
draft_email_replies: 'Email',
+ email_auto_translate: 'Email',
learn_sender_signatures: 'Email',
check_email_urgency: 'Email',
daily_brief: 'Assistant',
@@ -678,6 +682,7 @@ function _renderTaskChips() {
const _TASK_CACHE_LABELS = {
summarize_emails: 'email summaries',
draft_email_replies: 'AI reply drafts',
+ email_auto_translate: 'email translations',
extract_email_events: 'email calendar cache',
learn_sender_signatures: 'sender signatures',
check_email_urgency: 'email tags',