Your email, simplified.
Mailbox is an AI-powered mail web application where the assistant controls the UI deterministically — not as a chatbot, but as a structured UI controller.
When a user says:
“Open the latest email from Cognizant”
The assistant:
- Navigates to Inbox
- Applies the correct filters
- Waits for inbox loading to finish
- Opens the correct email
- Confirms execution via the UI
The assistant does not narrate actions — it executes them visibly in the interface.
This project was built for the AI-Powered Mail Web Application Hiring Task .
The core requirement:
The AI must control the UI — not just respond in chat.
Mailbox satisfies this by implementing a deterministic, agentic architecture where:
- AI emits structured
UIAction[] - A central dispatcher executes them
- The UI reflects visible changes
- Task completion is confirmed only by execution
User
↓
Assistant Panel (Natural Language)
↓
/api/assistant (Intent Orchestration)
↓
Task Agent (Single Owner)
↓
UIAction[]
↓
dispatchAction (Execution Layer)
↓
Zustand Store + Next.js Router + Gmail API
↓
Visible UI Changes
↓
Execution Feedback
The AI never touches the DOM.
The assistant:
- ❌ Never mutates UI state directly
- ❌ Never performs side effects
- ❌ Never assumes execution success
- ✅ Only emits structured actions
All mutations and side effects occur through a single execution layer.
This guarantees:
- Determinism
- Debuggability
- Replayability
- Async correctness
- Safety from hallucinated success
- Framework: Next.js (App Router)
- Language: TypeScript
- State Management: Zustand
- Mail Provider: Gmail API (OAuth2)
- AI: OpenAI (JSON-only structured output)
- Styling: Tailwind CSS
/app
/inbox
/sent
/compose
/email/[id]
/api
/auth
/gmail
/assistant
/components
AssistantPanel.tsx
/lib
assistant/
orchestrator.ts
aiContext.ts
buildAIContext.ts
agents/
navigationAgent.ts
filterTaskAgent.ts
filterApplyAgent.ts
readEmailTaskAgent.ts
emailTaskAgent.ts
replyEmailTaskAgent.ts
forwardEmailTaskAgent.ts
sendEmailAgent.ts
dispatchAction.ts
uiActions.ts
gmailClient.ts
tokenStore.ts
/store
uiStore.ts
-
Real Gmail inbox
-
Click to open email
-
AI-controllable filters:
- Sender
- Keyword
- Date range
- Read / unread
-
State-driven filtering
-
Near real-time sync via polling
- Full email rendering
- Reply and forward support
- Thread-aware replies
- Real Gmail sent mail
- Clickable → detail view
-
Fully state-driven
-
Works via:
- Manual UI interaction
- AI-assisted drafting
-
Real Gmail send
All AI control flows through:
type UIAction =
| { type: "OPEN_INBOX" }
| { type: "OPEN_SENT" }
| { type: "OPEN_COMPOSE" }
| { type: "OPEN_EMAIL"; payload: { id: string } }
| { type: "FILL_COMPOSE"; payload: { to?: string; subject?: string; body?: string } }
| { type: "SEND_EMAIL" }
| { type: "APPLY_FILTER"; payload: InboxFilters }
| { type: "CLEAR_FILTERS" };A single dispatchAction() function executes:
- Zustand state updates
- Route navigation
- Gmail API side effects
Humans and AI share the same execution path.
Mailbox implements a deterministic, agentic architecture .
The assistant classifies user intent into:
navigatefilterreademail_taskreplyforwardsendunknown
Exactly one task agent owns each request.
No monolithic “super-agent”.
Each agent owns a semantic responsibility:
navigationAgentfilterTaskAgentreadEmailTaskAgentemailTaskAgentreplyEmailTaskAgentforwardEmailTaskAgentsendEmailAgent
Agents:
- Plan actions
- Coordinate multi-step flows
- Never execute side effects
- Never declare success
Applying a filter does not mean inbox data is immediately available.
A naive system would guess or use timeouts.
Mailbox does not.
The UI exposes:
inboxLoading: boolean
Agents are forbidden from selecting emails while loading.
Flow:
Apply filter
→ inboxLoading = true
→ agent returns awaiting_load
→ inbox finishes loading
→ assistant re-enters
→ agent proceeds
This makes the assistant truly agentic, not heuristic.
- LLM-assisted drafting
- Multi-turn field completion
- Optional confirmation before sending
-
Valid only in email detail view
-
Injects:
threadIdmessageIdreferences
-
LLM detects send intent (even with typos)
-
Execution layer confirms success
- No threading metadata
- Mechanical transformation
- Lightweight and fast
- Optional auto-send
sendEmailAgent performs no reasoning.
It sends whatever is currently in compose.
Agents never declare success.
Only the execution layer emits:
taskFeedback
Assistant UI reacts to real outcomes.
This prevents:
- Duplicate sends
- Hallucinated success
- Optimistic UI errors
-
OAuth2 flow implemented
-
Scopes:
gmail.readonlygmail.send
-
Tokens stored in memory (demo trade-off)
GET /api/gmail/inboxGET /api/gmail/sentGET /api/gmail/email/[id]POST /api/gmail/send
git clone https://github.com/mg2660/MAiLBox
cd mailbox
npm install
Create .env.local:
GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret
OPENAI_API_KEY=your_openai_key
NEXT_PUBLIC_BASE_URL=http://localhost:3000
Enable Gmail API in Google Cloud Console.
npm run dev
Open:
http://localhost:3000
Login via Gmail OAuth.
Include:
- Screenshot of inbox
- Screenshot of assistant filling compose
- Screenshot of filter execution
- Short Loom video (recommended)
- Simpler implementation
- Not production-ready
- Requires re-auth on server restart
- Simpler to implement
- Sufficient for demo
- Pub/Sub integration possible next
LLM is used only where reasoning adds value:
- Intent classification
- Filter interpretation
- Email drafting
- Reply body extraction
Not used for:
- Navigation
- Execution
- Mechanical transformations
- ✅ Real emails sent & received
- ✅ Inbox & Sent display real data
- ✅ Compose works manually
- ✅ AI fills compose form
- ✅ AI filters inbox visibly
- ✅ AI navigates & opens emails
- ✅ Context-aware reply
- ✅ Async-safe agent re-entry
- ✅ Execution-confirmed completion
- Gmail Pub/Sub push sync
- Attachments support
- Reply-all
- Thread grouping view
- Persistent token storage
- Unit tests for agents
- Visual action preview panel
The key idea behind Mailbox:
The assistant proposes intent. The application decides truth.
By separating:
- Planning
- Execution
- Confirmation
Mailbox achieves true agentic UI control, not a chat overlay.