DEV Community

Cover image for MailMate AI Desktop — Open-Source Email Assistant That Summarizes, Prioritizes, and Creates Tasks Locally
Praveen Kumar
Praveen Kumar

Posted on AI-assisted

MailMate AI Desktop — Open-Source Email Assistant That Summarizes, Prioritizes, and Creates Tasks Locally

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

Most people manage their inbox by reading every email and deciding what matters. That's the problem.

MailMate AI Desktop flips this: two open-weight models read your Gmail first, summarize every email into 1–2 sentences, classify it by priority, and deliver only what needs your attention — directly to your Telegram. Your data never leaves your machine.

Who would pay for this: Freelancers, consultants, indie founders, and anyone whose inbox is a productivity tax. A tool that reads 80 emails and surfaces the 3 that matter — with context — is easily worth $10–20/month. The local-first approach makes it viable for anyone who can't feed client emails to a cloud API.


The Pipeline

Gmail (read-only)
  → Normalize email
  → Qwen2.5-0.5B via vLLM   ← summarize to 1-2 sentences  (local, free)
  → Laya typed-decision engine ← classify actionability + priority  (local CPU)
  → Application-side policy  ← decide what is permitted
  → SQLite                   ← persist if approved
  → Telegram bot             ← summary + notification / Yes/No confirmation
  → Google Calendar          ← separate explicit step, never automatic
Enter fullscreen mode Exit fullscreen mode

Two open-weight models, two jobs:

Model Job Runs on
Qwen/Qwen2.5-0.5B-Instruct Summarize each email into 1–2 sentences Local vLLM (CPU)
convaiinnovations/laya (typed-decisions) Classify actionability + priority Local CPU

The summarizer runs first. Laya then classifies the summary — not the raw email. Cleaner signal, better decisions.


Priority System

Priority Meaning What happens
🔴 P0 Critical incident / immediate risk Task created automatically
🟠 P1 Important with a clear deadline Asks for your confirmation
🟡 P2 Routine actionable Asks for your confirmation
⚪ OTHER Informational Summary only

Key safety rule: A model prediction alone never authorises anything. An explicit policy module enforces the rules: ambiguous deadlines are never auto-scheduled, P1/P2 tasks require your confirmation, calendar events are always a separate explicit step.


Demo

Step 1 — Bot starts up, /start shows the full priority system:

MailMate AI /start welcome message showing P0 auto-creates tasks, P1/P2 ask confirmation, OTHER summarizes only, with available commands listed

Step 2 — First Gmail poll: 10 real inbox emails summarized and classified. AI-generated summary shown for each email. Footer: "Processed 10 emails — no tasks":

MailMate AI Email Summary showing inbox emails grouped under Updates, each with priority label and AI-generated summary in italics. Processed 10 emails — no tasks.

Step 3 — User sends /poll from Telegram after new test emails arrive. Bot responds immediately: "Polling Gmail now...":

Bottom of email summary batch visible, user sends /poll, bot responds Polling Gmail now...

Step 4 — /poll results. P0 critical security breach: task created automatically + Google Calendar event scheduled at 09:00 IST. P1 design review: AI summary shown, deadline extracted, Yes/No confirmation buttons appear:

P0 Security breach task auto-created with calendar event. P1 Design review showing AI-generated summary, deadline 2026-10-28, and Yes create task / No skip buttons

Step 5 — User clicks "Yes, create task". Task confirmed instantly. Google Calendar event created for P1 at 09:00 IST on 2026-10-28:

Task confirmed message for P1 Design review. Calendar event created at 09:00 IST on 2026-10-28 shown immediately after confirmation

Step 6 — Full batch summary for the /poll run. Important/Tasks section shows P0 (auto-created) and P1 (awaiting confirmation). Updates section shows remaining emails with AI summaries:

MailMate AI Email Summary showing Important/Tasks section with P0 and P1, followed by Updates section with OTHER emails and AI summaries

Step 7 — Google Calendar showing both events created from the pipeline:

Google Calendar showing two auto-created events from MailMate AI pipeline

Step 8 — vLLM running Qwen2.5-0.5B locally, processing emails on CPU, zero cloud cost:

Terminal showing vLLM inference logs with token generation statistics per email

Step 9 — macOS menu bar app: ✉ icon in the tray, click to see active tasks and trigger a poll:

macOS menu bar showing MailMate AI tray icon with dropdown: P0 Security breach task listed in red, 1 pending task, Poll now and Quit options


Why Open Innovation Made This Possible

Zero running cost. Both models run locally on a MacBook Intel — no API bills, no per-token charges. That's what makes a Side Hustle viable: the marginal cost of processing 100 emails a day is electricity.

Complete privacy. Your inbox never touches a third-party server. OAuth tokens stay on disk. The SQLite database is local. For freelancers handling client emails, this is non-negotiable.

Swappable components. Because both models are open-weight and self-hosted, I can swap the summarizer for a better model as they improve, fine-tune Laya on my own email patterns, or run the whole stack on a Raspberry Pi. Closed APIs don't give you that.

Explicit policy, not prompt magic. The decision logic is a readable Python module with a clear routing table — not a prompt hoping a model will "be careful." You can read it, audit it, change it. That's only possible because the classification is a typed-decision engine, not a generative black box.


Build Session

This project was built end-to-end in a single Claude Code session — 10 milestones, 365 tests, live integration testing with real Gmail and Telegram.

Building MailMate AI Desktop — Local-First Email Assistant with Two Open-Weight Models
You

Let's go ahead with the planner file and build MailMate AI Desktop — a local-first email assistant that summarizes, prioritizes, and creates tasks from Gmail.

Agent

Starting with the project planner inspection and reference repo analysis. The PI version uses HuggingFace generative models and systemd — we'll replace those with Laya (typed-decision engine) and a Python-native pipeline.

You

Fix the deadline state — I want NONE, CLEAR, AMBIGUOUS instead of a boolean.

Agent

Added DeadlineState enum and updated decide() with new routing: P0 always creates task (any deadline), P1/P2 need CLEAR deadline for confirmation, AMBIGUOUS → summarize only. 14 tests passing.

You

Go ahead with Milestone 2 — SQLite task store.

Agent

Built TaskStore with idempotency via UNIQUE source_email_id constraint. DuplicateEmailError on reprocessing. In-memory mode uses persistent connection for tests. 26 tests passing.

You

Go ahead with Milestone 3 — connect Laya to policy.

Agent

Built ClassificationResult dataclass, classify_email() wrapper, and apply_policy() firewall. All Laya calls mocked in tests — no model loads. 78 tests passing. Key safety: model predictions alone cannot create tasks.

You

Go ahead with Milestone 4 — email normalization and task extraction.

Agent

Built NormalizedEmail and TaskCandidate models. Regex-based deadline detector: ISO dates → CLEAR, relative terms (tomorrow, ASAP, next Friday, bare day names) → AMBIGUOUS, nothing → NONE. Multiple dates → AMBIGUOUS. 134 tests passing.

You

Go ahead with Milestone 5 — orchestration and idempotency.

Agent

Built process_email() pipeline: idempotency check → summarize → detect deadline → classify → policy → persist → return result. dry_run=True default. notify=True only after successful DB commit. 167 tests passing.

You

Go ahead with Milestone 6 — Telegram notifications.

Agent

Built NotificationTransport ABC, MockTransport, TelegramTransport. P2 confirmation via inline buttons: confirm:<task_id> / decline:<task_id>. handle_callback() with 48h timeout window. Declined tasks stay DECLINED, cannot be re-confirmed. 212 tests passing.

You

Go ahead with Milestone 7 — Google Calendar.

Agent

Built CalendarTransport ABC with MockCalendarTransport and GoogleCalendarTransport (live_writes=True gate). schedule_event() runs 6 eligibility checks before any API call: duplicate, declined status, non-CLEAR deadline, missing deadline, missing time, missing timezone. 245 tests passing.

You

Go ahead with Milestone 8 — Gmail.

Agent

Built GmailTransport ABC, MockGmailTransport, GmailClient with gmail.readonly scope. MIME parser handles text/plain, text/html, multipart/alternative (prefers plain), multipart/mixed. fetch_new_emails() pre-filters by email_has_task() before fetching body. 276 tests passing.

You

Go ahead with Milestone 9 — desktop UI.

Agent

Built macOS menu bar app via rumps. Tray icon ✉/⏳/⚠ cycles with pipeline state. P2 tasks show Confirm/Decline sub-menus. Confirm/Decline/Complete all go through existing confirmation_handler. Timer-based polling with threading lock. 300 tests passing.

You

Go ahead with Milestone 10 — holdout validation.

Agent

Built 24-example human-reviewed holdout set. End-to-end integration tests covering duplicate processing, failure recovery, declined confirmation safety, ambiguous-deadline safety, notification guard. 361 tests passing.

You

Let's change the logic — P0 always creates a task, P1/P2 need a clear deadline for confirmation, otherwise summarize.

Agent

Updated policy routing table: P0 + any deadline → CREATE_TASK_AND_NOTIFY. P1/P2 + CLEAR → ASK_CONFIRMATION. P1/P2 + NONE/AMBIGUOUS → SUMMARIZE_ONLY. Removed CLARIFY_DEADLINE as a policy outcome — simpler, more intuitive. 365 tests passing.

You

Add AI email summarization — I want actual summaries not just subject lines.

Agent

Found vLLM running locally with Qwen/Qwen2.5-0.5B-Instruct at localhost:8000. Built summarizer.py using OpenAI-compatible API. Pipeline updated: summarize first → pass summary to Laya (cleaner signal). Summary shown in every Telegram notification — batch summary, task created, P2 confirmation. HF Inference API as fallback (requires PRO credits). 365 tests passing.

You

Run live mode — send test emails, trigger /poll from Telegram.

Agent

Live pipeline confirmed end-to-end: Gmail fetch → vLLM summary → Laya classification → policy → SQLite → Telegram (batch summary + task notifications + P2 Yes/No) → Google Calendar event creation. P0 Security breach → task auto-created + calendar event. P1 Design review → AI summary shown + Yes/No buttons → confirmed → calendar event created. P2 meeting notes → summary only. OTHER → summary only.


Tech Stack

  • Qwen/Qwen2.5-0.5B-Instruct via vLLM — local summarisation
  • convaiinnovations/laya (typed-decisions) — actionability + priority
  • Python 3.11 — pipeline, policy, storage
  • SQLite — local task store with idempotency (365 tests)
  • Telegram Bot API — notifications + inline confirmation buttons
  • Google Calendar API — scheduling (explicit step only)
  • Gmail API (read-only) — inbox polling
  • rumps — macOS menu bar app

GitHub

GitHub logo praveen-pravi6B / mailmate-ai-desktop

Local-first macOS email assistant — Laya typed decisions, SQLite, Telegram, Google Calendar

MailMate AI Desktop

A local-first macOS email assistant that identifies actionable emails applies an explicit decision policy, and surfaces tasks via a menu bar app and Telegram — without sending data to any cloud backend.


What it does

Gmail (read-only)
  → Normalize email
  → HuggingFace Summarizer      ← human-readable summary of each email
  → Laya typed-decision engine  ← actionability + priority classification
  → Application-side policy     ← what is permitted
  → SQLite task store           ← persist if approved
  → Telegram notification       ← summary + notify / ask for confirmation
  → Google Calendar             ← separate explicit step, never automatic

Two models, two jobs:

Model Job Runs on
openai/gpt-oss-120b:fastest Summarize each email into 1-2 sentences HuggingFace Inference API
convaiinnovations/laya Classify actionability + priority Local CPU

The model classifies; the policy decides; you confirm anything routine. No model output can bypass that chain.

Priority levels

Priority Meaning Policy
P0 Critical incident / immediate serious risk
…

What I'd Build Next

  • A Telegram webhook loop so callbacks are instant
  • Fine-tune Laya on personal email patterns for better P2 accuracy
  • Windows/Linux tray support (replace rumps)
  • A paid tier with multi-account support

Top comments (0)