vellum-ai/vellum-assistant/skills/gmail/SKILL.md
gmail
Manage Gmail email — drafting, sending, organizing, filters, vacation replies, and inbox analysis
- Source repository stars
- 1,002
- Declared platforms
- 0
- Static risk flags
- 2
- Last source update
- 2026-08-04
- Source checked
- 2026-08-04
Decision brief
What it does—and where it fits
All operations use CLI scripts that return JSON:
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/vellum-ai/vellum-assistant --skill "skills/gmail"Inspect the Agent Skill "gmail" from https://github.com/vellum-ai/vellum-assistant/blob/32b93fb5c0f5e77d2a15e2f214d3726208c9d208/skills/gmail/SKILL.md at commit 32b93fb5c0f5e77d2a15e2f214d3726208c9d208. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
Review the staged operations
bun run scripts/gmail-runs.ts inspect --run-id ""
bun run scripts/gmail-runs.ts inspect --run-id "" - 02
Connection Setup
1. Check connection health first. Run assistant oauth status google. This checks whether the user's Google account is connected and the token is valid. 2. If no connection is found or the status check fails: Load the vellum-oauth-integrations skill. The skill will evaluate wheth…
Check connection health first. Run assistant oauth status google. This checks whether the user's Google account is connected and the token is valid.If no connection is found or the status check fails: Load the vellum-oauth-integrations skill. The skill will evaluate whether managed or your-own mode is appropriate and guide the user accordingly.1. Check connection health first. Run assistant oauth status google. This checks whether the user's Google account is connected and the token is valid. 2. If no connection is found or the status check fails: Load the ve… - 03
Workflow
1. Scan: Run bun run scripts/gmail-scan.ts sender-digest. Default query targets promotions currently in the inbox from the last 90 days (in:inbox category:promotions newerthan:90d). The script returns a cachekey plus a lightweight sender summary (counts, unsubscribe availability…
Scan: Run bun run scripts/gmail-scan.ts sender-digest. Default query targets promotions currently in the inbox from the last 90 days (in:inbox category:promotions newerthan:90d). The script returns a cachekey plus a lig…Present: Show results as a table with selectionMode: "multiple":Columns (exactly 3): Sender, Emails Found, Unsub? - 04
Workflow integration
1. Before scanning: Run bun run scripts/gmail-prefs.ts --action list. If blocklisted senders exist, offer to auto-archive them first ("I have N previously archived senders — want me to clean those up first?"). Remove safelisted senders from scan results before presenting the tab…
Before scanning: Run bun run scripts/gmail-prefs.ts --action list. If blocklisted senders exist, offer to auto-archive them first ("I have N previously archived senders — want me to clean those up first?"). Remove safel…After archiving: Run bun run scripts/gmail-prefs.ts --action add-blocklist with the archived sender emails to persist them for future sessions.After user deselects: When the user deselects senders from a cleanup table, run bun run scripts/gmail-prefs.ts --action add-safelist with the deselected sender emails. - 05
Date Verification
Before composing any email that references a date or time:
Check the currenttime: field in the block for today's date and timezoneVerify that "tomorrow" means the day after today's date, "next week" means the upcoming Monday–Friday, etc.If the email references a date from another message, cross-check it against the turn context to ensure it's in the future
Permission review
Static risk signals and limitations
Writes files
The documentation asks the agent to create, modify, or delete local files.
| `gmail-email.ts` | `draft` | Create email drafts in the Drafts folder (including reply drafts) |Runs scripts
The documentation asks the agent to run terminal commands or scripts.
bun run scripts/gmail-email.ts draft --to "[email protected]" --subject "Hello" --body "Message body"Runs scripts
The documentation asks the agent to run terminal commands or scripts.
bun run scripts/gmail-email.ts draft --to "[email protected]" --subject "Re: Hello" --body "Reply body" --thread-id "18f..." --in-reply-to "18f..."Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 88/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 1,002 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- vellum-ai/vellum-assistant
- Skill path
- skills/gmail/SKILL.md
- Commit
- 32b93fb5c0f5e77d2a15e2f214d3726208c9d208
- License
- MIT
- Collected
- 2026-08-04
- Default branch
- main
View the original SKILL.md
This skill provides Gmail-specific operations beyond the shared messaging skill. For cross-platform messaging (send, read, search, reply), use the messaging skill. Gmail operations depend on the messaging skill's provider infrastructure - load messaging first if Gmail is not yet connected.
Script Reference
All operations use CLI scripts that return JSON:
- Success:
{ "ok": true, "data": ... } - Failure:
{ "ok": false, "error": "..." }
| Script | Operation | Description |
|---|---|---|
gmail-email.ts | draft | Create email drafts in the Drafts folder (including reply drafts) |
gmail-email.ts | send-draft | Send an existing draft (requires explicit user confirmation) |
gmail-email.ts | forward | Create forward drafts, preserving attachments |
gmail-email.ts | trash | Move messages to Trash |
gmail-manage.ts | label | Add or remove labels on messages |
gmail-manage.ts | follow-up | Track/untrack messages for follow-up using a dedicated "Follow-up" label |
gmail-manage.ts | attachments | List and download email attachments |
gmail-manage.ts | filters | Create, list, and delete Gmail filters |
gmail-manage.ts | vacation | Get, enable, or disable the vacation auto-responder |
gmail-manage.ts | unsubscribe | Unsubscribe from mailing lists (requires explicit user confirmation) |
gmail-scan.ts | sender-digest | Scan inbox and group messages by sender for declutter workflows |
gmail-scan.ts | outreach-scan | Identify cold outreach senders (no List-Unsubscribe header) |
gmail-archive.ts | archive | Archive messages (single, batch message_ids, cache_key+sender-emails, query) |
gmail-archive.ts | archive --dry-run | Preview what would be archived without executing (writes staged ops to log) |
gmail-archive.ts | archive --resume | Resume an interrupted archive run from its last checkpoint |
gmail-commit.ts | commit | Execute all staged ops from a dry-run |
gmail-commit.ts | cancel | Delete a run log without executing anything |
gmail-runs.ts | list | List recent operation runs with status summaries |
gmail-runs.ts | inspect | Show detailed log entries for a specific run |
gmail-runs.ts | prune | Delete operation logs older than 30 days |
gmail-reverse.ts | --run-id | Reverse all committed ops in a run (un-archive, un-label, un-trash) |
gmail-reverse.ts | --run-id --thread | Reverse a specific message within a committed run |
gmail-prefs.ts | list | List blocklist and safelist preferences |
gmail-prefs.ts | add-blocklist | Add sender emails to the blocklist |
gmail-prefs.ts | add-safelist | Add sender emails to the safelist |
gmail-prefs.ts | remove-blocklist | Remove sender emails from the blocklist |
gmail-prefs.ts | remove-safelist | Remove sender emails from the safelist |
gmail-prefs.ts | get-management-config | Get inbox management config (stage, interrupt threshold, last run) |
gmail-prefs.ts | set-management-config | Update inbox management config (--stage, --interrupt-threshold, --last-run) |
Email Operations
# Draft an email
bun run scripts/gmail-email.ts draft --to "[email protected]" --subject "Hello" --body "Message body"
# Draft a reply in a thread
bun run scripts/gmail-email.ts draft --to "[email protected]" --subject "Re: Hello" --body "Reply body" --thread-id "18f..." --in-reply-to "18f..."
# Send an existing draft (REQUIRES user confirmation before execution)
bun run scripts/gmail-email.ts send-draft --draft-id "r123..."
# Forward a message
bun run scripts/gmail-email.ts forward --message-id "18f..." --to "[email protected]" --text "FYI"
# Trash a message
bun run scripts/gmail-email.ts trash --message-id "18f..."
Management Operations
# Label - add a label to a message
bun run scripts/gmail-manage.ts label --message-id "18f..." --add-labels "Work,Important"
# Follow-up - track a message for follow-up
bun run scripts/gmail-manage.ts follow-up --action track --message-id "18f..."
# Attachments - list attachments on a message
bun run scripts/gmail-manage.ts attachments --action list --message-id "18f..."
# Attachments - download a specific attachment
bun run scripts/gmail-manage.ts attachments --action download --message-id "18f..." --attachment-id "ANGj..." --filename "report.pdf"
# Filters - create a Gmail filter
bun run scripts/gmail-manage.ts filters --action create --from "[email protected]" --remove-labels "INBOX"
# Vacation - enable auto-responder
bun run scripts/gmail-manage.ts vacation --action enable --message "I'm out of office until Monday"
# Unsubscribe from a mailing list (REQUIRES user confirmation before execution)
bun run scripts/gmail-manage.ts unsubscribe --message-id "18f..."
Scan Operations
# Sender digest - scan inbox grouped by sender
bun run scripts/gmail-scan.ts sender-digest [--query "in:inbox category:promotions newer_than:90d"]
# Outreach scan - identify cold outreach senders
bun run scripts/gmail-scan.ts outreach-scan [--time-range "90d"]
Scan scripts store message IDs in the assistant's cache and return a lightweight summary with a cache_key. This keeps thousands of message IDs out of the conversation context. To retrieve cached message IDs for a specific sender:
assistant cache get <cache_key> --json
# Returns: { "ok": true, "data": { "[email protected]": ["msgId1", "msgId2", ...], ... } }
Archive Operations
# Archive using cache_key + sender emails (preferred for declutter workflows)
bun run scripts/gmail-archive.ts archive --cache-key "scan:abc123" --sender-emails "[email protected],[email protected]"
# Archive specific message IDs
bun run scripts/gmail-archive.ts archive --message-ids "18f1...,18f2...,18f3..."
# Archive a single message
bun run scripts/gmail-archive.ts archive --message-id "18f..."
# Archive by query
bun run scripts/gmail-archive.ts archive --query "from:[email protected] in:inbox"
Operation Runs
All destructive archive operations are logged to a JSONL operation log for resumability and auditing. Each batch of archives is tracked as a "run" with a unique ID.
# List recent runs with status summaries
bun run scripts/gmail-runs.ts list [--limit 20]
# Show detailed log entries for a specific run
bun run scripts/gmail-runs.ts inspect --run-id "run_20260420_a1b2c3d4"
# Delete logs older than 30 days
bun run scripts/gmail-runs.ts prune
Dry-Run Mode
Dry-run mode runs the full pipeline (scanning, collecting message IDs) but skips all destructive API calls. Staged entries are written to the op log for review.
# Preview what would be archived
bun run scripts/gmail-archive.ts archive --query "..." --dry-run
# Review the staged operations
bun run scripts/gmail-runs.ts inspect --run-id "<run-id>"
# Commit the staged operations (executes the archive)
bun run scripts/gmail-commit.ts commit --run-id "<run-id>"
# Cancel (delete the log, nothing executed)
bun run scripts/gmail-commit.ts cancel --run-id "<run-id>"
Label and filter operations also support --dry-run:
bun run scripts/gmail-manage.ts label --message-ids "..." --add-labels "..." --dry-run
bun run scripts/gmail-manage.ts filters --action create --from "..." --remove-labels "INBOX" --dry-run
Archive operations now return a run_id in their output. Use this to resume interrupted runs:
# Resume an interrupted run (e.g. after daily quota hit)
bun run scripts/gmail-archive.ts archive --resume "run_20260420_a1b2c3d4"
# Pass --run-id to group multiple archive calls under one run
bun run scripts/gmail-archive.ts archive --query "..." --run-id "run_20260420_a1b2c3d4" --phase "noise_archive"
When a run is interrupted (e.g. Gmail daily quota exceeded), the operation log records the interruption with a resume hint. The assistant should detect interrupted runs and offer to resume them rather than starting fresh.
Reversing a Run
If a committed run archived messages incorrectly, reverse it:
# Reverse all committed operations in a run (requires confirmation)
bun run scripts/gmail-reverse.ts --run-id "run_20260420_a1b2c3d4"
# Reverse a specific message within a run (no confirmation needed)
bun run scripts/gmail-reverse.ts --run-id "run_20260420_a1b2c3d4" --thread "18f..."
Reversal semantics:
- archive → adds INBOX label back (un-archives)
- label_add → removes the labels that were added
- label_remove → adds back the labels that were removed
- trash → removes TRASH label and adds INBOX (un-trashes, within Gmail's 30-day window)
- filter_create → not auto-reversible; delete manually via
gmail-manage.ts filters --action delete
Reversals are themselves logged as runs (auditable, resumable). The reversal only touches labels/state that the original run modified — it does not touch user-applied labels.
Preferences Operations
# List all preferences
bun run scripts/gmail-prefs.ts --action list
# Add senders to blocklist
bun run scripts/gmail-prefs.ts --action add-blocklist --emails "[email protected],[email protected]"
# Add senders to safelist
bun run scripts/gmail-prefs.ts --action add-safelist --emails "[email protected]"
# Remove from blocklist
bun run scripts/gmail-prefs.ts --action remove-blocklist --emails "[email protected]"
# Remove from safelist
bun run scripts/gmail-prefs.ts --action remove-safelist --emails "[email protected]"
Email Routing Priority
When the user mentions "email" - sending, reading, checking, decluttering, drafting, or anything else - always default to the user's own email (Gmail) unless they explicitly ask about the assistant's own email address (e.g., "set up your email", "send from your address", "check your inbox"). The vast majority of email requests are about the user's Gmail, not the assistant's @vellum.me address.
Do not offer the assistant's own email as an option unless the user specifically asks. If Gmail is not connected, guide them through Gmail setup.
Connection Setup
Gmail
- Check connection health first. Run
assistant oauth status google. This checks whether the user's Google account is connected and the token is valid. - If no connection is found or the status check fails: Load the
vellum-oauth-integrationsskill. The skill will evaluate whether managed or your-own mode is appropriate and guide the user accordingly.
Error Recovery
When a Gmail script fails with a token or authorization error:
- Try to reconnect silently. Run
assistant oauth ping google. This often resolves expired tokens automatically. - If reconnection fails, go straight to setup. Don't present options, ask which route the user prefers, or explain what went wrong technically. Just tell the user briefly (e.g., "Gmail needs to be reconnected - let me set that up") and immediately load the
vellum-oauth-integrationsskill. The user came to you to get something done, not to troubleshoot - make it seamless. - Never try alternative approaches. Don't use curl, browser automation, or any workaround. If the scripts can't do it, the reconnection flow is the answer.
- Never expose error details. The user doesn't need to see error messages about tokens, OAuth, or API failures. Translate errors into plain language.
Safety: Send-Draft and Unsubscribe
Two operations are high-risk and require explicit user confirmation before execution:
send-draft: Always show the user what will be sent (recipients, subject, body summary) and wait for explicit confirmation ("yes", "send it", etc.) before running the send-draft script. The script gates onassistant ui confirm— it will present a confirmation dialog to the user and only proceed if approved. Never auto-send.unsubscribe: Always tell the user which mailing list will be unsubscribed from and wait for explicit confirmation before running the unsubscribe script. The script gates onassistant ui confirm. Unsubscribe actions cannot be undone.
If the user has not explicitly confirmed, do not execute these operations. A general instruction like "clean up my inbox" is not confirmation to unsubscribe - it means scan and present options.
Drafting vs Sending (Gmail)
Gmail uses a draft-first workflow. All compose and reply operations create Gmail drafts automatically:
bun run scripts/gmail-email.ts draftcreates a draft in Gmail Draftsbun run scripts/gmail-email.ts draftwith--thread-idand--in-reply-tocreates a threaded reply draftbun run scripts/gmail-email.ts forwardcreates a forward draft, preserving attachments- The messaging skill's send operation with Gmail also creates a draft
To actually send: Use bun run scripts/gmail-email.ts send-draft with the draft ID after the user has reviewed it. Only run send-draft when the user explicitly says "send it" or equivalent.
Email Threading (Gmail)
When replying to or continuing an email thread:
- Preferred: Use the messaging skill's send with the thread's
thread_id— it automatically handles threading, reply-all recipients, and subject lines. - Manual drafting: Use
bun run scripts/gmail-email.ts draftwith both--thread-idand--in-reply-tofor full control. Thethread_idplaces the draft in the correct Gmail thread;in_reply_tosets the RFC 822 threading headers. - Getting the Message-ID: Search and read results include
rfc822MessageIdin message metadata (looks like<[email protected]>). This is the value to pass as--in-reply-to. You can also pass a Gmail message ID directly — the draft script auto-resolves it to the RFC 822 header.
Gmail Search Syntax
When searching Gmail, the query uses Gmail's search operators:
| Operator | Example | What it finds |
|---|---|---|
from: | from:[email protected] | Messages from a specific sender |
to: | to:[email protected] | Messages sent to a recipient |
subject: | subject:meeting | Messages with a word in the subject |
newer_than: | newer_than:7d | Messages from the last 7 days |
older_than: | older_than:30d | Messages older than 30 days |
is:unread | is:unread | Unread messages |
has:attachment | has:attachment | Messages with attachments |
label: | label:work | Messages with a specific label |
Email Decluttering
When a user asks to declutter, clean up, or organize their email - start scanning immediately. Don't ask what kind of cleanup they want or request permission to read their inbox. Go straight to scanning - but once results are ready, always present them and let the user choose actions before archiving or unsubscribing.
CRITICAL: Never archive, unsubscribe, or take similar bulk actions unless the user has explicitly confirmed for that specific batch. Each batch of results requires its own explicit user confirmation. If the user says "keep going" or "keep decluttering," that means scan and present new results - NOT auto-archive. Previous batch approvals do not carry forward, but deselections DO carry forward: when the user deselects senders from a cleanup batch, run bun run scripts/gmail-prefs.ts --action add-safelist with those sender emails. Before building the next cleanup table, run bun run scripts/gmail-prefs.ts --action list and exclude safelisted senders from the table — the user already indicated they want to keep those.
Inbox Recon (run before cleanup passes)
Before starting category-specific cleanup, understand the inbox:
- Broad scan: Run
bun run scripts/gmail-scan.ts sender-digest --query "in:inbox"with--max-senders 75. This surfaces the top senders across ALL categories — not just promotions. - Identify cleanup buckets: Group the results mentally:
- Newsletters/promotions (
hasUnsubscribe: true) → handle in promotions pass - Mailing lists / automated forwards (group addresses like
devops@,alerts@,noreply@) → handle in general noise pass - Reminder chains (DocuSign, GCP, Auth0 — same sender, many messages) → handle in general noise pass
- Automated receipts (Brex, Stripe, OpenAI credits) → handle in general noise pass
- Cold outreach (unfamiliar senders, no unsubscribe) → handle in outreach pass
- Real correspondence → skip
- Newsletters/promotions (
- Design passes: Use the recon results to decide which passes to run and in what order. If 80% of the inbox is promotions, start there. If the biggest bucket is mailing-list forwards, start with a targeted query for that address.
- General noise pass: After promotions and before outreach, run
bun run scripts/gmail-scan.ts sender-digest --query "in:inbox -category:promotions -category:personal"to catch mailing lists, reminder chains, and automated notifications that aren't categorized as promotions. Present as a table following the same pattern as the promotions pass. Pre-select automated/noise senders, deselect anything that looks like real correspondence.
Workflow
- Scan: Run
bun run scripts/gmail-scan.ts sender-digest. Default query targets promotions currently in the inbox from the last 90 days (in:inbox category:promotions newer_than:90d). The script returns acache_keyplus a lightweight sender summary (counts, unsubscribe availability, sample subjects). Message IDs are stored in the assistant's cache — do NOT ask for them unless needed for archiving. Counts shown in the table reflect only what is currently in the inbox — these are the emails that will be archived. - Present: Show results as a table with
selectionMode: "multiple":- Columns (exactly 3): Sender, Emails Found, Unsub?
- Unsub? cell values: Use rich cell format:
{ "text": "Yes", "icon": "checkmark.circle.fill", "iconColor": "success" }whenhasUnsubscribeis true,{ "text": "No", "icon": "minus.circle", "iconColor": "muted" }when false.
- Unsub? cell values: Use rich cell format:
- Pre-select all rows (
selected: true) - users deselect what they want to keep - Caption: Include two parts separated by a newline: (1) data scope, e.g. "Newsletters, notifications, and outreach from last 90 days. Deselect anything you want to keep." (adjusted to match the query used), and (2) the Unsub? column legend: "Unsub? - "Yes" means these emails contain an unsubscribe link, so I can opt you out automatically. "No" means no unsubscribe link was found - these will be archived but you may continue receiving them."
- Action buttons (exactly 2): "Archive & Unsubscribe" (primary), "Archive Only" (secondary). NEVER offer Delete, Trash, or any destructive action.
- Columns (exactly 3): Sender, Emails Found, Unsub?
- Embed cache_key in button data: When constructing the action buttons, include the
cache_keyfrom the scan result in each button'sdatafield. This ensurescache_keyis forwarded automatically when the user clicks — the LLM does not need to recall it from earlier context:{ "id": "archive_unsubscribe", "label": "Archive & Unsubscribe", "style": "primary", "data": { "cache_key": "<cache_key value here>" } } - Wait for user action: Stop and wait. Do NOT proceed to archiving or unsubscribing until the user explicitly confirms which senders to clean up and which action to take. When the user clicks an action button you will receive an action message containing
action data: { cache_key, selectedIds }:selectedIdsare sender IDs (base64-encoded email addresses) — NOT Gmail message IDs. Always use them as sender emails withcache_key, never asmessage_ids.- Show progress with steps for each phase (e.g., "Archiving 89 senders (2,400 emails)", "Unsubscribing from 72 senders"). Update each step as each phase finishes.
- Act on selection - batch, don't loop:
- Archive all at once: Run
bun run scripts/gmail-archive.ts archive --skip-confirmonce with--cache-key(from action data) +--sender-emailsset to all selected sender emails. The script resolves message IDs from the cache and batches the Gmail API calls internally - never loop sender-by-sender. Use--skip-confirmbecause the user already confirmed via the UI table. - Unsubscribe in bulk: If the action is "Archive & Unsubscribe", run
bun run scripts/gmail-manage.ts unsubscribefor each sender that hashasUnsubscribe: true— but emit all unsubscribe calls in a single assistant response (parallel tool use) rather than one-at-a-time across separate turns.
- Archive all at once: Run
- Accurate summary: The scan counts are exact - the
messageCountshown in the table matches the number of messages archived. Format: "Cleaned up [total_archived] emails from [sender_count] senders. Unsubscribed from [unsub_count]." - Ongoing protection offer: After reporting results, offer auto-archive filters:
- "Want me to set up auto-archive filters so future emails from these senders skip your inbox?"
- If yes, run
bun run scripts/gmail-manage.ts filters --action createfor each sender with--fromset to the sender's email and--remove-labels "INBOX". - Then offer a recurring declutter schedule: "Want me to scan for new clutter monthly?"
Cold Outreach Cleanup
After the newsletter/promotions pass, offer to clean up cold outreach — unsolicited emails from senders without unsubscribe links. This catches sales pitches, recruiting spam, and mass outreach that newsletter filters miss.
- Scan: Run
bun run scripts/gmail-scan.ts outreach-scan(default: last 90 days, senders withoutList-Unsubscribeheaders). The scan includes ahasPriorReplyflag per sender — true means the user has previously replied to that sender. - Filter out known contacts: Exclude senders where
hasPriorReply: true— these are conversations, not cold outreach. If thecontactsskill is loaded, also cross-reference against Google Contacts and exclude matches. - Classify senders using sample subjects, email domains, and message patterns. Categorize into:
- Clear junk (pre-select for archive): loan/LOC offers, generic SaaS pitches, mass marketing from unknown domains, senders with random/concatenated domain names
- Sales outreach (pre-select for archive): targeted product pitches with personalised subject lines ("Hi [name]", "for [company]"), outreach tool domains (apollo.io, outreach.io, lemlist.com, instantly.ai, etc.)
- Potentially useful (deselect / keep by default): recruiting, investor outreach, partnership proposals, vendor introductions that reference the user's specific product or role
- Ambiguous (deselect / keep by default): anything you're not confident about
- Present as a table following the same pattern as the newsletter workflow. Use two visual sections:
- Pre-selected rows: clear junk + sales outreach
- Deselected rows: potentially useful + ambiguous senders (user reviews these)
- Caption: "Cold outreach from the last 90 days (senders without unsubscribe links). Pre-selected senders look like spam or sales pitches. Deselected senders may be useful — review before archiving."
- Archive on user action: Same flow as newsletter cleanup — wait for explicit user confirmation, then batch archive.
Key principle: Not all cold outreach is unwanted. Recruiting, investor, and partnership emails can be valuable. When uncertain, default to keeping the sender (deselected) and let the user decide.
Large Inbox Handling
When a scan returns truncated: true or timeBudgetExceeded: true, the inbox has more messages than a single scan pass can cover. Split subsequent scans by date range to ensure full coverage:
Pass 1: in:inbox older_than:90d (oldest backlog)
Pass 2: in:inbox newer_than:90d older_than:30d (recent months)
Pass 3: in:inbox newer_than:30d older_than:7d (recent weeks)
Pass 4: in:inbox newer_than:7d (this week)
Merge results from all passes before presenting the final table. Each pass covers a smaller window, reducing per-scan message count and avoiding timeouts. Only split when a scan actually reports truncation — most inboxes are handled fine in a single pass.
Edge Cases
- Zero results: Tell the user "No newsletter emails found" and suggest broadening the query (e.g. removing the category filter or extending the date range)
- Unsubscribe failures: Report per-sender success/failure; the unsubscribe script handles edge cases
- Truncation handling: The scan covers up to 1,000 messages by default (cap 2,000). If
truncatedis true, the top senders are still captured. Offer to run additional date-range passes to cover the remaining messages (see Large Inbox Handling above). - Time budget exceeded: If the scan returns
timeBudgetExceeded: true, present whatever results were collected. Offer to run additional date-range passes for uncovered periods.
Cleanup Preferences (Blocklist & Safelist)
The gmail-prefs.ts script persists sender preferences across cleanup sessions:
- Blocklist: Sender emails archived in previous sessions. On future cleanups, pre-pass archive all blocklisted senders before scanning (use
bun run scripts/gmail-archive.ts archive --query "from:email1 OR from:email2 ... in:inbox"). The user will see a confirmation prompt for the archive — once approved, the script proceeds. - Safelist: Sender emails the user explicitly deselected (chose to keep). Exclude these senders from future cleanup tables entirely.
Workflow integration
- Before scanning: Run
bun run scripts/gmail-prefs.ts --action list. If blocklisted senders exist, offer to auto-archive them first ("I have N previously archived senders — want me to clean those up first?"). Remove safelisted senders from scan results before presenting the table. - After archiving: Run
bun run scripts/gmail-prefs.ts --action add-blocklistwith the archived sender emails to persist them for future sessions. - After user deselects: When the user deselects senders from a cleanup table, run
bun run scripts/gmail-prefs.ts --action add-safelistwith the deselected sender emails. - User overrides: If the user asks to stop blocking or stop keeping a sender, use
remove-blocklistorremove-safelistaccordingly.
Company-Domain Sender Handling
When scan results include senders from the user's own company domain (e.g., @vellum.ai):
- Protect individual colleagues: Cofounders, direct reports, teammates sending from their personal work address (e.g.,
[email protected],[email protected]) — deselect these from cleanup tables. - Archive mailing lists and group addresses: Addresses like
devops@,alerts@,noreply@,security@,billing@at the company domain are automated forwards, not personal correspondence. These are archivable by default — pre-select them in the cleanup table. - When uncertain: If a company-domain sender could be either a colleague or a mailing list (e.g.,
[email protected]), check sample subjects. Automated alerts, CI notifications, and vendor forwards → archivable. Direct messages with personal context → protect.
Do not blanket-protect an entire domain. The user's company domain often generates more automated noise than any external sender.
Scan Operations
Scan scripts (gmail-scan.ts sender-digest, gmail-scan.ts outreach-scan) return a cache_key that references message IDs stored in the assistant's cache. This keeps thousands of message IDs out of the conversation context.
- Pass
cache_key+ sender emails tobun run scripts/gmail-archive.ts archiveinstead of individualmessage_ids - Cache entries expire after 30 minutes. When a cache entry expires, archiving automatically falls back to query-based archiving per sender.
- To retrieve cached data:
assistant cache get <cache_key> --json - Raw
message_idsstill work as a fallback for non-scan workflows
The outreach-scan operation enriches each sender with hasPriorReply (whether the user has ever sent an email to that address). Use this signal to filter out legitimate correspondents before classifying cold outreach.
Batch Operations
bun run scripts/gmail-archive.ts archivesupports--cache-key+--sender-emails(preferred for declutter workflows),--message-ids,--message-id, or--query.bun run scripts/gmail-manage.ts labelsupports--message-idor--message-idsonly — it does not acceptcache_key.- First scan to get a
cache_key, then use the archive script to batch-archive by sender. - Always confirm with the user before batch operations on large numbers of messages.
Attachments
- List attachments:
bun run scripts/gmail-manage.ts attachments --action list --message-id "18f..."— returns filename, MIME type, size, and attachment ID for each attachment on a message. - Download attachment:
bun run scripts/gmail-manage.ts attachments --action download --message-id "18f..." --attachment-id "ANGj..." --filename "report.pdf"— saves a specific attachment to disk.
Workflow: use attachments --action list to discover attachments, then attachments --action download to save them locally.
Common Workflows
Declutter Inbox
- Run
bun run scripts/gmail-scan.ts sender-digestto scan for newsletters and promotions - Present results showing senders, message counts, and unsubscribe availability
- Wait for user to select senders and confirm an action
- Archive selected senders and unsubscribe where available (if user chose "Archive & Unsubscribe")
- Offer to create filters for ongoing protection
Create a Gmail Filter
- User says "auto-archive emails from [email protected]"
- Run
bun run scripts/gmail-manage.ts filters --action create --from "[email protected]" --remove-labels "INBOX" - Confirm the filter was created and explain what it does
Set Vacation Auto-Reply
- User says "set my out-of-office for next week"
- Run
bun run scripts/gmail-manage.ts vacation --action enable --message "I'm out of office until Monday" - Confirm the auto-reply is active and when it expires
Manage Follow-ups
- User says "flag this email for follow-up" — run
bun run scripts/gmail-manage.ts follow-up --action track --message-id "18f..." - User says "what emails am I tracking?" — run
bun run scripts/gmail-manage.ts follow-up --action list - User says "mark that as done" — run
bun run scripts/gmail-manage.ts follow-up --action untrack --message-id "18f..."
Identify Cold Outreach
- Run
bun run scripts/gmail-scan.ts outreach-scanto find senders without unsubscribe headers - Present results for review — these are likely cold outreach or unsolicited emails
- User can choose to archive, create filters to block, or ignore
Date Verification
Before composing any email that references a date or time:
- Check the
current_time:field in the<turn_context>block for today's date and timezone - Verify that "tomorrow" means the day after today's date, "next week" means the upcoming Monday–Friday, etc.
- If the email references a date from another message, cross-check it against the turn context to ensure it's in the future
Confidence Scores
Medium and high risk operations require a confidence score between 0 and 1:
- 0.9-1.0: User explicitly requested this exact action
- 0.7-0.8: Action is strongly implied by context
- 0.5-0.6: Reasonable inference but some ambiguity
- Below 0.5: Ask the user to confirm before proceeding
Alternatives
Compare before choosing
XiaomiMiMo/MiMo-Code
index
Use this Sales index first for explicit Sales mentions and clear seller workflows: prospecting, lead qualification, account research, monitoring or prioritization, meeting prep, call follow-up, outreach research, deal strategy, pipeline or forecast review, CRM-backed context or data enrichment, internal source finding or sales support, customer quotes or evidence, business cases, competitive briefs, rep coaching, sales company research, and company or contact enrichment. For implicit use require
zgbrenner/agentcounsel
Antitrust Risk Intake
Use when facts with possible competition-law exposure first arrive — a sales email about a competitor contact, a proposed collaboration, a pricing move, a hotline report, or an agency inquiry — to produce a draft conduct inventory and triage matrix that buckets each item, flags any time-critical track, and routes each item to the matched antitrust deep-dive skill for attorney review, without classifying conduct or assessing legality.
XiaomiMiMo/MiMo-Code
zoominfo
Use only when a focused Sales workflow has selected a present and connected ZoomInfo connector, or the user explicitly asks for ZoomInfo company search, contact search, enrichment, intent, similarity, recommendations, or research. Do not use when another enrichment source is selected.
browser-act/skills
amazon-best-selling-products-finder-api-skill
This skill helps users extract structured best-selling product data from Amazon via the BrowserAct API. Agent should proactively apply this skill when users express needs like search for best selling products on Amazon, extract Amazon product data based on keywords, find top rated Amazon products, monitor Amazon competitor prices and sales, discover trending products on Amazon marketplace, extract Amazon product titles prices and ratings, gather Amazon product sales volume for market research, s