Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .dev.vars.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
# Cloudflare Access (required in production). TEAM_DOMAIN may be the base Access URL or the full /cdn-cgi/access/certs URL.
POLICY_AUD=your-access-policy-audience-tag
TEAM_DOMAIN=https://your-team.cloudflareaccess.com
# Optional Google Drive import. Share selected files with this service account.
GOOGLE_SERVICE_ACCOUNT_EMAIL=service-account@example.iam.gserviceaccount.com
GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n"
# Optional Microsoft Graph app-only OneDrive import.
MICROSOFT_TENANT_ID=your-tenant-id
MICROSOFT_CLIENT_ID=your-app-client-id
MICROSOFT_CLIENT_SECRET=your-app-client-secret
ONEDRIVE_USER_ID=person@example.com
# Microsoft OAuth (required for the Outlook connector)
MICROSOFT_TENANT_ID=common
MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
MICROSOFT_REDIRECT_URI=http://localhost:5173/auth/microsoft/callback
APP_ORIGIN=http://localhost:5173
# Generate a separate random value for production token encryption.
TOKEN_ENCRYPTION_KEY=
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ https://github.com/cloudflare/agentic-inbox/issues/4#issuecomment-4269118513
- **Built-in AI agent** — Side panel with 9 email tools for reading, searching, drafting, and sending
- **Auto-draft on new email** — Agent automatically reads inbound emails and generates draft replies, always requiring explicit confirmation before sending
- **Configurable and persistent** — Custom system prompts per mailbox, persistent chat history, streaming markdown responses, and tool call visibility
- **Evidence-backed memory** — Markdown/file memory with keyword and optional semantic retrieval, source provenance, confirmed facts, Google Drive imports, and operator-visible drafting context

## Stack

Expand All @@ -62,6 +63,55 @@ npm run dev
1. Set your domain in `wrangler.jsonc`
2. Create an R2 bucket named `agentic-inbox`: `wrangler r2 bucket create agentic-inbox`

### Optional cloud-drive memory imports

Configure `GOOGLE_SERVICE_ACCOUNT_EMAIL` and `GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY` as Worker secrets, then share selected Drive files with that service account. In Memory, choose **Google Drive** and paste the selected file IDs. Imports are mailbox-scoped, deduplicated by Drive file ID, converted to Markdown-compatible text, chunked, and indexed through the same pipeline as uploaded files.

For Microsoft OneDrive, configure `MICROSOFT_TENANT_ID`, `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET`, and `ONEDRIVE_USER_ID` as Worker secrets. Grant the Microsoft Entra app the least-privilege Microsoft Graph application permission needed to read the configured user drive, then choose **Microsoft OneDrive** in Memory and paste selected file IDs. OneDrive imports use the same normalization, chunking, provenance, deduplication, and fact-review pipeline.

### Outlook + Power Automate workflows

The recommended no-Premium workflow uses standard Outlook email actions and the existing Cloudflare Email Routing receiver:

```
Outlook: When a new email arrives (V3)
-> Forward an email (V2) to the Agentic Inbox address
Cloudflare Email Routing
-> Agentic Inbox Worker receives and stores the message
-> EmailAgent + Workers AI create an Agentic Inbox draft
-> Operator reviews and sends from Agentic Inbox
```

This path does not require Microsoft OAuth tokens in Cloudflare, OneDrive, or the Premium HTTP connector. Configure the forwarding destination as a mailbox already created in Agentic Inbox, for example `ai@example.com`, and ensure Cloudflare Email Routing forwards that address to this Worker.

#### Optional Premium HTTP bridge

The separately deployed bridge is available at:

```text
https://outlook-ai-bridge.shorlol.workers.dev/integrations/power-automate/outlook
```

It accepts an authenticated JSON POST and returns parsed `classification`, `priority`, `draftSubject`, `draftBodyHtml`, and `confidence` fields. It is intended for Power Automate's Premium **HTTP** action and does not persist messages or send mail:

```text
Outlook trigger -> Premium HTTP POST -> Workers AI JSON -> Outlook Create draft
```

Configure its secret with `npx wrangler secret put BRIDGE_SECRET --name outlook-ai-bridge`. Do not use the Office 365 Users or Office 365 Outlook **Send an HTTP request** actions as substitutes: those actions are Microsoft Graph-specific, not general external HTTP clients.

#### Outlook Drafts without Premium HTTP

Putting the AI result into the Outlook Drafts folder without Premium HTTP requires a second email relay that is not yet part of the Worker:

```text
Outlook -> standard Forward action -> Agentic Inbox + Workers AI
-> Worker sends a structured [Agentic Draft] result email
-> Outlook trigger -> Parse JSON -> Draft an email message
```

The required backend addition is a Worker result-email step containing the original recipient, generated subject, and generated HTML body. The second standard Outlook flow can then create the real Outlook draft and optionally delete the temporary result email. Until that relay is implemented, AI drafts are available in Agentic Inbox only.

### Deploy

```bash
Expand Down
132 changes: 132 additions & 0 deletions add-in/manifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Replace YOUR-ADDIN-HOST.example.com with the HTTPS host that serves /add-in/. -->
<OfficeApp
xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0"
xsi:type="MailApp">
<Id>c4ae0c81-89f5-40d3-b8f8-e1eaaef82f66</Id>
<Version>1.0.0.0</Version>
<ProviderName>Cloudflare</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Agentic Inbox" />
<Description DefaultValue="Open Agentic Inbox with Outlook message context." />
<IconUrl DefaultValue="https://YOUR-ADDIN-HOST.example.com/favicon.ico" />
<HighResolutionIconUrl DefaultValue="https://YOUR-ADDIN-HOST.example.com/favicon.ico" />
<SupportUrl DefaultValue="https://YOUR-ADDIN-HOST.example.com/" />
<AppDomains>
<AppDomain>https://YOUR-ADDIN-HOST.example.com</AppDomain>
</AppDomains>
<Hosts>
<Host Name="Mailbox" />
</Hosts>
<Requirements>
<Sets>
<Set Name="Mailbox" MinVersion="1.5" />
</Sets>
</Requirements>
<FormSettings>
<Form xsi:type="ItemRead">
<DesktopSettings>
<SourceLocation DefaultValue="https://YOUR-ADDIN-HOST.example.com/add-in/taskpane.html?hostContext=read&amp;appRoot=current&amp;appPath=/" />
<RequestedHeight>320</RequestedHeight>
</DesktopSettings>
</Form>
<Form xsi:type="ItemEdit">
<DesktopSettings>
<SourceLocation DefaultValue="https://YOUR-ADDIN-HOST.example.com/add-in/taskpane.html?hostContext=compose&amp;appRoot=current&amp;appPath=/" />
<RequestedHeight>320</RequestedHeight>
</DesktopSettings>
</Form>
</FormSettings>
<Permissions>ReadWriteItem</Permissions>
<Rule xsi:type="RuleCollection" Mode="Or">
<Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" />
<Rule xsi:type="ItemIs" ItemType="Message" FormType="Edit" />
</Rule>
<DisableEntityHighlighting>false</DisableEntityHighlighting>
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Description resid="Commands.Description" />
<Requirements>
<bt:Sets DefaultMinVersion="1.5">
<bt:Set Name="Mailbox" />
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<FunctionFile resid="Taskpane.Read.Url" />
<ExtensionPoint xsi:type="MessageReadCommandSurface">
<OfficeTab id="TabDefault">
<Group id="ReadGroup">
<Label resid="Commands.GroupLabel" />
<Control xsi:type="Button" id="ReadOpenTaskpane">
<Label resid="Commands.ReadButtonLabel" />
<Supertip>
<Title resid="Commands.ReadButtonLabel" />
<Description resid="Commands.ReadButtonDescription" />
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16" />
<bt:Image size="32" resid="Icon.32" />
<bt:Image size="80" resid="Icon.80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="Taskpane.Read.Url" />
<SupportsPinning>true</SupportsPinning>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
<ExtensionPoint xsi:type="MessageComposeCommandSurface">
<OfficeTab id="TabDefault">
<Group id="ComposeGroup">
<Label resid="Commands.GroupLabel" />
<Control xsi:type="Button" id="ComposeOpenTaskpane">
<Label resid="Commands.ComposeButtonLabel" />
<Supertip>
<Title resid="Commands.ComposeButtonLabel" />
<Description resid="Commands.ComposeButtonDescription" />
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16" />
<bt:Image size="32" resid="Icon.32" />
<bt:Image size="80" resid="Icon.80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="Taskpane.Compose.Url" />
<SupportsPinning>true</SupportsPinning>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16" DefaultValue="https://YOUR-ADDIN-HOST.example.com/favicon.ico" />
<bt:Image id="Icon.32" DefaultValue="https://YOUR-ADDIN-HOST.example.com/favicon.ico" />
<bt:Image id="Icon.80" DefaultValue="https://YOUR-ADDIN-HOST.example.com/favicon.ico" />
</bt:Images>
<bt:Urls>
<bt:Url id="Taskpane.Read.Url" DefaultValue="https://YOUR-ADDIN-HOST.example.com/add-in/taskpane.html?hostContext=read&amp;appRoot=current&amp;appPath=/" />
<bt:Url id="Taskpane.Compose.Url" DefaultValue="https://YOUR-ADDIN-HOST.example.com/add-in/taskpane.html?hostContext=compose&amp;appRoot=current&amp;appPath=/" />
</bt:Urls>
<bt:ShortStrings>
<bt:String id="Commands.GroupLabel" DefaultValue="Agentic Inbox" />
<bt:String id="Commands.ReadButtonLabel" DefaultValue="Open Inbox" />
<bt:String id="Commands.ComposeButtonLabel" DefaultValue="Open Inbox" />
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="Commands.Description" DefaultValue="Open Agentic Inbox with the current Outlook item context." />
<bt:String id="Commands.ReadButtonDescription" DefaultValue="Inspect the current message and open Agentic Inbox." />
<bt:String id="Commands.ComposeButtonDescription" DefaultValue="Inspect the draft and open Agentic Inbox." />
</bt:LongStrings>
</Resources>
</VersionOverrides>
</OfficeApp>
Loading