npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tuttiai/email

v0.1.0

Published

Tutti voice — read and send email via IMAP IDLE + SMTP; shared client for @tuttiai/inbox

Readme

@tuttiai/email

Email voice for Tutti — gives agents the ability to read inbound mail (IMAP IDLE) and send outbound mail with proper threading (SMTP). Also exports the shared EmailClientWrapper that @tuttiai/inbox's email adapter consumes.

Three tools ship: send_email and send_reply are marked destructive: true; list_inbox is read-only.

Install

tutti-ai add email
# or
npm install @tuttiai/email

Setup

The voice needs IMAP and SMTP credentials. Host, port, user and TLS settings live in the score; passwords come from env vars:

| Var | Used for | |---|---| | TUTTI_EMAIL_PASSWORD | Shared fallback for both IMAP and SMTP. Use this when both are the same password (Gmail, IONOS, most providers). | | TUTTI_EMAIL_IMAP_PASSWORD | IMAP-only override. | | TUTTI_EMAIL_SMTP_PASSWORD | SMTP-only override. |

For Gmail / Outlook with 2FA, basic auth is disabled — generate an app-specific password.

Score example

import { EmailVoice } from "@tuttiai/email";
import { defineScore } from "@tuttiai/core";

export default defineScore({
  agents: {
    support: {
      name: "support",
      system_prompt: "You are an email support agent. Read incoming mail and send threaded replies.",
      voices: [
        new EmailVoice({
          imap: { host: "imap.example.com", port: 993, user: "[email protected]" },
          smtp: { host: "smtp.example.com", port: 587, user: "[email protected]" },
          from: "Tutti Bot <[email protected]>",
        }),
      ],
      permissions: ["network"],
    },
  },
});

Tools

| Tool | Description | |---|---| | send_email | Send a fresh email. Single string or array of recipients; cc / bcc supported. | | send_reply | Reply with proper In-Reply-To and References headers. The in_reply_to Message-ID comes from list_inbox (or from the inbox event payload). | | list_inbox | List recent messages (Message-ID, from, subject, date). Defaults to UNSEEN, capped at 50 entries. |

Inbound (inbox)

The wrapper exposes subscribeMessage(handler) powered by IMAP IDLE — no polling, the server pushes new mail. @tuttiai/inbox's EmailInboxAdapter consumes this. A score that wires both the voice (outbound tools) and the inbox adapter (inbound) shares one EmailClientWrapper via EmailClientWrapper.forKey("host:port:user", …) — one IMAP connection, one SMTP transporter.

The wrapper drops messages oversized via the IMAP SIZE flag before parsing (default 1 MB plain text, configurable via maxBodyChars), and runs SecretsManager.redact on the dispatched body by default to keep accidental leaked credentials out of agent traces. Set redactRawText: false to opt out.

Lifecycle

The IMAP connection is established lazily on the first subscribeMessage / list_inbox / explicit launch() call, and stays open with IDLE-driven push. SMTP transports are pooled by nodemailer and created on first send. Call voice.teardown() (or TuttiRuntime.teardown()) on shutdown — both connections are closed cleanly.

License

Apache-2.0.