AECS-1 (AI Email Consumption Specification) is an open standard for deterministically normalizing raw RFC 5322/MIME email into AI-ready JSON — the schema, the threading algorithm, the six content levels, and the rules for cleaning them without losing the sender's words. It is published under CC0 1.0 (public domain): implement it in any language, without asking permission.
@mvrx/aecs is the framework-agnostic, zero-infrastructure-dependency MIT reference
implementation of that spec: parsing, content cleaning, threading, and message
normalization, with no Cloudflare or storage assumptions baked in.
- AECS-1 specification — the normative document
- AECS-SDK-1 specification — the target SDK surface for implementations
- JSON Schema — machine-checkable
NormalizedEmailshape - Conformance suite — fixtures + an independent checker (
verify.py) - Docs site — browsable version of the above
- Storage guides — tables, keys and indexes for Cloudflare D1/R2, SQLite, PostgreSQL, MySQL, MongoDB and DynamoDB (
examples/storage/) - Changelog — what changed in each release
npm install @mvrx/aecsimport { parse } from "@mvrx/aecs";
const email = await parse(rawRfc5322Message);
console.log(email.messageId); // normalized Message-ID
console.log(email.threadId); // deterministic thread identifier
console.log(email.content.clean); // quote-stripped, signature-stripped body
console.log(email.content.forAI); // wrapped, AI-ready representationparse() accepts a raw message as a string, ArrayBuffer, Uint8Array, or a
ReadableStream<Uint8Array>.
| Field | What it holds |
|---|---|
content.raw / content.text |
The full body, quoted history included |
content.clean |
Quoted history and signatures removed. The sender's own lines are always kept, including answers written below or between quotes |
content.forAI |
clean, normalised and bounded for an LLM |
Cleanup follows AECS-1 §4.3.1.
If it would leave nothing, clean falls back to text and processing.cleanFallback
is true. forAI is still a lossy view: if your app acts on the body, keep
content.text as a fallback.
Ask decision models typed questions about an email and get typed answers with probabilities back, not free text. See AECS-SDK-1 §6.3.
import { clefProvider, decideEmail, jevProvider } from "@mvrx/aecs/decisions";
const { answers } = await decideEmail(email, clefProvider(env.AI), {
route: { type: "choice", instructions: "Which team?", criteria: { billing: null, support: null } },
urgent: { type: "noul", instructions: "Is this urgent?" },
});
answers.route.choice; // "billing" | "support"
answers.urgent.noul; // 0..1MIT. See LICENSE.