Bounded classification
Which category? How urgent? Does it need attention? Jev returns typed judgments and probabilities.
JEV FOR INBOX ROUTING
Start with five unsorted emails. One Jev evaluation classifies each message, then application policy decides which ones can be routed automatically and which need review.
01 / LIVE INBOX
Choose a sample inbox, edit any of the five emails, then let Jev route them to work queues.
Names are presentation. Stable internal IDs map Jev choices back to your queues.
Queues: Billing · Technical · Sales · Account · General · Spam / Archive
Create a setup or use a preset, then run Jev.
05 / FROM INBOX TO QUEUES
Which category? How urgent? Does it need attention? Jev returns typed judgments and probabilities.
Move? Archive? Create a ticket? Notify someone? Ask a human? A Jev probability is not authorization to delete, archive, or send.
This Jev Playground demo performs no external action. Jevmail's current Gmail scope deliberately does not authorize Gmail write actions. A different production application may add them only through separate permissions and deterministic policy.
06 / WHY PROBABILITY MATTERS
A selected queue is useful, but an application also needs a policy for confidence. This demo auto-routes only when the returned selected probability meets the adjustable threshold. A lower value or missing distribution goes to Manual Review.
The email appears under its chosen operational queue. This is still a model estimate, not guaranteed correctness.
The reviewer sees Jev's selected queue, its probability, and the runner-up when available. Review is deliberate workflow handling, not a failed request.
02 / REAL GMAIL
Jev Playground deliberately does not request access to your inbox. If you want to run this pattern against real Gmail data, an open-source project called Jevmail implements a local, read-only Gmail workflow.
Best for trying Jev classification on your own Gmail account without giving the app permission to send, archive, label, or delete messages.
gmail.readonly| Capability | Jev Playground Email Triage | Jevmail |
|---|---|---|
| Inbox | Simulated / user-edited | Real Gmail |
| OAuth | None | Google OAuth |
| Gmail permission | None | gmail.readonly |
| Jev call | Real | Real |
| Sends email | No | No under current scope |
| Modifies Gmail | No | No under current scope |
| Local mailbox cache | No | SQLite in local app |
| Best for | Designing routing policy | Testing on a real Gmail inbox |
03 / RUN JEVMAIL LOCALLY
These are Jevmail requirements, not requirements for using the Jev Playground demo: Node.js 22.18+, pnpm, a Google account, a Google Cloud project with a Web OAuth client, and Vercel AI Gateway access.
Source checked at f6f20af. That tracked tree does not contain .env.example, so the verified steps below create .env.local directly.
http://localhost:3000/api/auth/callback/googleAlways compare these steps with the current upstream README because OAuth configuration can change.
git clone https://github.com/fazlerocks/jevmail
cd jevmail
git checkout f6f20af9c2805efd924c29cbea59547e72c558fc
pnpm installInstallation does not require Gmail access or a Jev call.
.env.local without overwriting itGenerate a local Auth.js secret, then copy its output into the AUTH_SECRET placeholder:
openssl rand -base64 32if [ -e .env.local ]; then
printf '%s\n' '.env.local already exists; edit it instead.'
else
cat > .env.local <<'EOF'
AUTH_SECRET=<paste output from openssl rand -base64 32>
AUTH_GOOGLE_ID=<Google OAuth client ID>
AUTH_GOOGLE_SECRET=<Google OAuth client secret>
AI_GATEWAY_API_KEY=<Vercel AI Gateway API key>
EOF
fiThe command keeps an existing .env.local unchanged. Replace all four placeholders locally. Do not print, commit, or share the resulting credentials.
pnpm devOpen http://localhost:3000. Reaching the login page confirms the local app started; it does not prove Google OAuth or real email classification has been completed.
Google's External Testing publishing status uses short-lived test refresh-token behavior; Google currently documents a seven-day expiry when broader scopes such as Gmail access are requested. Expect periodic re-authentication and check Google's current OAuth documentation before production use.
04 / PERMISSIONS & DATA FLOW
The current Jevmail project requests only https://www.googleapis.com/auth/gmail.readonly. This is an OAuth permission boundary enforced by Google for the granted token.
gmail.readonlyRead-only Gmail does not mean the email content stays local. Classification requires sending the selected email text to the configured Jev provider. OAuth credentials and tokens, local SQLite data, corrections, and application state stay with the locally running app under the current architecture.
07 / EMAIL TRIAGE ARCHITECTURES
multiple emails + shared queue definitions↓one Jev evaluation · one Choice per email↓local review thresholdGmail fetch → one message state↓tray + urgency + personalness↓store probabilities and correctionsNeither structure is universally better. They illustrate different batching and application designs. The Jev overview explains the typed primitives behind both.
08 / BUILD THE CLASSIFICATION LAYER
This original TypeScript example mirrors the Playground workflow: assemble a bounded queue catalog and five user-edited emails, call typesafe-ai/jev once, validate every answer, then apply an application-owned threshold. It contains no Gmail or external mail API code.
import { createGateway, experimental_evaluate as evaluate } from 'ai';
const gateway = createGateway({ apiKey: process.env.AI_GATEWAY_API_KEY });
const QUEUES = {
billing: 'Payments, invoices, refunds, and subscription billing.',
technical: 'Product faults, errors, broken features, and technical failures.',
sales: 'Pre-purchase pricing, demos, procurement, and commercial evaluation.',
account: 'Identity, ownership, access administration, and account recovery.',
general: 'Legitimate operational mail that fits no other queue.',
spam_archive: 'Scams, unsolicited promotions, bulk mail, or no-action newsletters.',
};
export async function triageInbox(emails: Array<{
id: string; from: string; subject: string; body: string;
}>) {
if (emails.length !== 5) throw new Error('This workflow requires five emails');
const state = [
'TASK',
'Route each email to exactly one operational queue.',
'',
'QUEUE DEFINITIONS',
...Object.entries(QUEUES).map(([id, definition]) => `${id}: ${definition}`),
'',
'INCOMING EMAILS',
...emails.flatMap((email) => [
'', `EMAIL ${email.id}`, `FROM: ${email.from}`,
`SUBJECT: ${email.subject}`, 'BODY:', email.body,
]),
].join('\n');
if (state.length > 8_000) throw new Error('Inbox is too large');
const questions = Object.fromEntries(emails.map((email, index) => [
`email_${index + 1}`,
{
type: 'choice' as const,
instructions: `Which work queue should email ${email.id} be routed to?`,
criteria: QUEUES,
},
]));
const result = await evaluate({
model: gateway.evaluationModel('typesafe-ai/jev'),
state,
questions,
maxRetries: 0,
abortSignal: AbortSignal.timeout(30_000),
providerOptions: { gateway: { zeroDataRetention: true } },
});
const AUTO_ROUTE_THRESHOLD = 0.8; // Application policy.
return emails.map((email, index) => {
const answer = result.answers[`email_${index + 1}`];
if (!answer || answer.type !== 'choice' || !(answer.choice in QUEUES)) {
throw new Error(`Invalid routing answer for ${email.id}`);
}
const probability = answer.probabilities?.[answer.choice];
return {
email,
queue: answer.choice,
probabilities: answer.probabilities,
status: probability !== undefined && probability >= AUTO_ROUTE_THRESHOLD
? 'AUTO_ROUTE'
: 'MANUAL_REVIEW',
};
});
}For server-side credentials, public errors, and provider setup, continue to the Jev API guide.
10 / OTHER IMPLEMENTATION PATTERNS
Useful for studying Choice routing and workflow handlers without a real mailbox integration.
Inspect the repository ↗Useful when emails already exist in application code and only need bounded classification against a shared catalog.
Inspect jev-mcp ↗This demo uses editable sample mail and never connects to Gmail or changes a real mailbox.
09 / PRIVACY & PRODUCTION BOUNDARIES
Mailbox access exposes private correspondence. Request the least privilege needed and send only the minimum fields required for classification.
OAuth client secrets, refresh tokens, and Jev or Gateway keys belong outside client bundles, logs, and source control.
Google classifies gmail.readonly as Restricted. A public product can require OAuth verification and, when restricted-scope data is stored on or transmitted through servers, a security assessment.
Do not let a high probability trigger irreversible external actions without tested deterministic policy, authorization checks, and appropriate review.
Jevmail is an independent side project. It is not affiliated with Jev Playground, TypeSafe AI, Vercel, Google, or their products. Follow its source and Google's current requirements before using real mailbox data.
11 / SOURCES
Verified on .