Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ec71ccc
chore: update readme
itsskofficial May 12, 2025
b0fb822
chore: update readme
itsskofficial May 12, 2025
5a23778
chore: update README.md
itsskofficial May 16, 2025
79c6304
chore: update README.md
itsskofficial May 16, 2025
a324179
Merge branch 'master' of https://github.com/existence-master/Sentient
Kabeer2004 Jul 6, 2025
ff89433
Introducing Sentient Cloud (merging current staging codebase into mas…
Kabeer2004 Jul 6, 2025
d925ca3
Merge branch 'staging'
itsskofficial Jul 9, 2025
a874f6e
Merge branch 'staging'
itsskofficial Jul 9, 2025
0359dc4
Merge branch 'staging'
itsskofficial Jul 16, 2025
c782266
Update README.md
itsskofficial Jul 28, 2025
441c487
Update README.md
itsskofficial Jul 28, 2025
34c4a2d
fix (memories): unauthorized issue
itsskofficial Aug 7, 2025
191edec
Merge branch 'staging'
itsskofficial Aug 7, 2025
47ac66b
Merge branch 'development' into staging
itsskofficial Aug 7, 2025
89704a2
Merge staging into master
itsskofficial Aug 7, 2025
6784aab
Merge branch 'development' into staging
itsskofficial Aug 7, 2025
7cee67e
Merge branch 'development' into staging
itsskofficial Aug 8, 2025
1ce428a
Merge branch 'development' into staging
itsskofficial Aug 8, 2025
4083e34
Merge branch 'development' into staging
itsskofficial Aug 8, 2025
ac7481b
Merge branch 'development' into staging
itsskofficial Aug 8, 2025
6dc6d38
Merge branch 'development' into staging
itsskofficial Aug 8, 2025
fee3641
Merge branch 'development' into staging
itsskofficial Aug 8, 2025
ebbb7e2
Merge branch 'development' into staging
itsskofficial Aug 8, 2025
9cf5db6
Merge branch 'staging' of https://github.com/existence-master/Sentien…
itsskofficial Aug 8, 2025
b120a17
Merge branch 'development' into staging
itsskofficial Aug 8, 2025
250ba6d
Merge branch 'development' into staging
itsskofficial Aug 10, 2025
6fcc17e
Merge branch 'staging'
itsskofficial Aug 10, 2025
dfc2536
Merge branch 'development' into staging
itsskofficial Aug 11, 2025
0d81e0b
Merge branch 'development' into staging
itsskofficial Aug 11, 2025
ca2fae3
Merge branch 'development' into staging
itsskofficial Aug 11, 2025
00a3644
Merge branch 'development' into staging
itsskofficial Aug 11, 2025
8de3255
Merge branch 'development' into staging
itsskofficial Aug 11, 2025
308c99a
fix (pwa): updated docker files
itsskofficial Aug 11, 2025
103ea0f
Merge branch 'development' into staging
itsskofficial Aug 11, 2025
7be763b
Merge branch 'staging'
itsskofficial Aug 11, 2025
b79da78
Merge branch 'development' into staging
itsskofficial Aug 12, 2025
3816162
Merge branch 'development' into staging
Kabeer2004 Aug 12, 2025
b8ca5dd
Merge branch 'development' into staging
itsskofficial Aug 14, 2025
8bc8965
Merge branch 'development' into staging
itsskofficial Aug 17, 2025
0dc4c63
Merge branch 'development' into staging
itsskofficial Aug 17, 2025
5b02ff5
Merge branch 'development' into staging
Kabeer2004 Aug 17, 2025
4c0e7cd
Merge branch 'development' into staging
itsskofficial Aug 18, 2025
d2c6464
Merge branch 'development' into staging
itsskofficial Aug 18, 2025
8c69cc0
Merge branch 'development' into staging
itsskofficial Aug 18, 2025
2a6ec3e
Merge branch 'staging'
itsskofficial Aug 18, 2025
0e1c835
feat: Add Outlook integration with Microsoft Graph API
AnshumanAI Aug 18, 2025
d4c07fb
Merge branch 'development' into staging
itsskofficial Aug 18, 2025
79a91f0
Merge branch 'staging'
itsskofficial Aug 18, 2025
c85215a
fix: Correct indentation error in routes.py for discord and outlook O…
AnshumanAI Aug 18, 2025
58ea570
Merge branch 'existence-master:master' into feature/outlook-integration
AnshumanAI Aug 18, 2025
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: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,16 @@
>
> Our goal is to give everyone personal super-intelligence.
>
> [Read our manifesto.](https://docs.google.com/document/d/1vbCGAbh9f8vXfPup_Z7cW__gnOLdRhEtHKyoIxJD8is/edit?tab=t.0#heading=h.2kit9yqvlc77)
> It acts as your central command center, bridging the gap between your goals and the actions required to achieve them. It is designed to be a truly proactive partner that understands you, manages your digital life, and gets things done—without you having to type long, complex prompts.
>
> It can:
> - **💬 Chat with you** about any topic via text or voice.
> - **🧠 Learn your preferences, habits, and goals** to better serve you over time.
> - **⚙️ Execute complex, multi-step tasks** and recurring workflows.
> - **🗓️ Proactively manage your day**, reading your emails and calendar to suggest schedules and remind you of important events.
> - **🔗 Integrate seamlessly** with the apps you use every day.
>
> For more information [read our manifesto.](https://docs.google.com/document/d/1vbCGAbh9f8vXfPup_Z7cW__gnOLdRhEtHKyoIxJD8is/edit?tab=t.0#heading=h.2kit9yqvlc77)

---

Expand Down Expand Up @@ -83,8 +92,7 @@
To access Sentient, head over to [our website.](https://sentient.existence.technology/)

### 🔒 Self-Hostable

The entire platform can be self-hosted and configured to run fully locally. [Check the relevant docs for more info.](https://sentient-2.gitbook.io/docs/getting-started/running-sentient-from-source-self-host)
The entire platform is open-source and can be self-hosted and configured to run fully locally, ensuring your data stays private. [Check the relevant docs for more info.](https://sentient-2.gitbook.io/docs/getting-started/running-sentient-from-source-self-host)

---

Expand Down Expand Up @@ -126,7 +134,7 @@ Distributed under the GNU AGPL License. See [LICENSE.txt](https://github.com/exi
<a href="https://github.com/itsskofficial">
<img src="https://avatars.githubusercontent.com/u/65887545?v=4?s=100" width="100px;" alt=""/>
<br />
<sub><b>itsskofficial</b></sub>
<sub><b>itsskofficial (Sarthak)</b></sub>
</a>
</td>
<td align="center">
Expand Down
8 changes: 8 additions & 0 deletions src/client/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ ARG AUTH0_CLIENT_ID
ARG AUTH0_CLIENT_SECRET
ARG AUTH0_AUDIENCE
ARG AUTH0_SCOPE
ARG MONGO_URI
ARG MONGO_DB_NAME
ARG VAPID_PRIVATE_KEY
ARG VAPID_ADMIN_EMAIL
ARG NEXT_PUBLIC_POSTHOG_KEY
ARG NEXT_PUBLIC_POSTHOG_HOST
ARG NEXT_PUBLIC_AUTH0_NAMESPACE
Expand All @@ -42,6 +46,10 @@ ENV AUTH0_CLIENT_ID=$AUTH0_CLIENT_ID
ENV AUTH0_CLIENT_SECRET=$AUTH0_CLIENT_SECRET
ENV AUTH0_AUDIENCE=$AUTH0_AUDIENCE
ENV AUTH0_SCOPE=$AUTH0_SCOPE
ENV VAPID_PRIVATE_KEY=$VAPID_PRIVATE_KEY
ENV VAPID_ADMIN_EMAIL=$VAPID_ADMIN_EMAIL
ENV MONGO_URI=$MONGO_URI
ENV MONGO_DB_NAME=$MONGO_DB_NAME
ENV NEXT_PUBLIC_POSTHOG_KEY=$NEXT_PUBLIC_POSTHOG_KEY
ENV NEXT_PUBLIC_POSTHOG_HOST=$NEXT_PUBLIC_POSTHOG_HOST
ENV NEXT_PUBLIC_AUTH0_NAMESPACE=$NEXT_PUBLIC_AUTH0_NAMESPACE
Expand Down
8 changes: 4 additions & 4 deletions src/client/app/api/memories/[memoryId]/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const PUT = withAuth(async function PUT(
{ params, authHeader }
) {
const { memoryId } = params
const backendUrl = new URL(`${appServerUrl}/api/memories/${memoryId}`)
const backendUrl = new URL(`${appServerUrl}/memories/${memoryId}`)

try {
const body = await request.json()
Expand All @@ -27,7 +27,7 @@ export const PUT = withAuth(async function PUT(
}
return NextResponse.json(data)
} catch (error) {
console.error(`API Error in /api/memories/${memoryId} (PUT):`, error)
console.error(`API Error in /memories/${memoryId} (PUT):`, error)
return NextResponse.json({ error: error.message }, { status: 500 })
}
})
Expand All @@ -37,7 +37,7 @@ export const DELETE = withAuth(async function DELETE(
{ params, authHeader }
) {
const { memoryId } = params
const backendUrl = new URL(`${appServerUrl}/api/memories/${memoryId}`)
const backendUrl = new URL(`${appServerUrl}/memories/${memoryId}`)

try {
const response = await fetch(backendUrl.toString(), {
Expand All @@ -51,7 +51,7 @@ export const DELETE = withAuth(async function DELETE(
}
return NextResponse.json(data)
} catch (error) {
console.error(`API Error in /api/memories/${memoryId} (DELETE):`, error)
console.error(`API Error in /memories/${memoryId} (DELETE):`, error)
return NextResponse.json({ error: error.message }, { status: 500 })
}
})
4 changes: 2 additions & 2 deletions src/client/app/api/memories/graph/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const appServerUrl =

export const GET = withAuth(async function GET(request, { authHeader }) {
// This new backend endpoint is assumed to exist and return { nodes: [], edges: [] }.
const backendUrl = new URL(`${appServerUrl}/api/memories/graph`)
const backendUrl = new URL(`${appServerUrl}/memories/graph`)

try {
const response = await fetch(backendUrl.toString(), {
Expand All @@ -22,7 +22,7 @@ export const GET = withAuth(async function GET(request, { authHeader }) {
}
return NextResponse.json(data)
} catch (error) {
console.error("API Error in /api/memories/graph:", error)
console.error("API Error in /memories/graph:", error)
return NextResponse.json({ error: error.message }, { status: 500 })
}
})
8 changes: 4 additions & 4 deletions src/client/app/api/memories/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const appServerUrl =
: process.env.NEXT_PUBLIC_APP_SERVER_URL

export const GET = withAuth(async function GET(request, { authHeader }) {
const backendUrl = new URL(`${appServerUrl}/api/memories`)
const backendUrl = new URL(`${appServerUrl}/memories`)

try {
const response = await fetch(backendUrl.toString(), {
Expand All @@ -22,13 +22,13 @@ export const GET = withAuth(async function GET(request, { authHeader }) {
}
return NextResponse.json(data)
} catch (error) {
console.error("API Error in /api/memories:", error)
console.error("API Error in /memories:", error)
return NextResponse.json({ error: error.message }, { status: 500 })
}
})

export const POST = withAuth(async function POST(request, { authHeader }) {
const backendUrl = new URL(`${appServerUrl}/api/memories`)
const backendUrl = new URL(`${appServerUrl}/memories`)
try {
const body = await request.json()
const response = await fetch(backendUrl.toString(), {
Expand All @@ -46,7 +46,7 @@ export const POST = withAuth(async function POST(request, { authHeader }) {
}
return NextResponse.json(data, { status: response.status })
} catch (error) {
console.error("API Error in /api/memories (POST):", error)
console.error("API Error in /memories (POST):", error)
return NextResponse.json({ error: error.message }, { status: 500 })
}
})
4 changes: 4 additions & 0 deletions src/client/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ services:
- AUTH0_CLIENT_SECRET=${AUTH0_CLIENT_SECRET}
- AUTH0_AUDIENCE=${AUTH0_AUDIENCE}
- AUTH0_SCOPE=${AUTH0_SCOPE}
- MONGO_URI=${MONGO_URI}
- MONGO_DB_NAME=${MONGO_DB_NAME}
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}
- NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST}
- NEXT_PUBLIC_AUTH0_NAMESPACE=${NEXT_PUBLIC_AUTH0_NAMESPACE}
- NEXT_PUBLIC_LANDING_PAGE_URL=${NEXT_PUBLIC_LANDING_PAGE_URL}
- NEXT_PUBLIC_VAPID_PUBLIC_KEY=${NEXT_PUBLIC_VAPID_PUBLIC_KEY}
- VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY}
- VAPID_ADMIN_EMAIL=${VAPID_ADMIN_EMAIL}
container_name: sentient-client
restart: unless-stopped
ports:
Expand Down
13 changes: 13 additions & 0 deletions src/server/main/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
DISCORD_CLIENT_SECRET = os.getenv("DISCORD_CLIENT_SECRET")
TODOIST_CLIENT_ID = os.getenv("TODOIST_CLIENT_ID")
TODOIST_CLIENT_SECRET = os.getenv("TODOIST_CLIENT_SECRET")
OUTLOOK_CLIENT_ID = os.getenv("OUTLOOK_CLIENT_ID")
OUTLOOK_CLIENT_SECRET = os.getenv("OUTLOOK_CLIENT_SECRET")

# --- WhatsApp ---
WAHA_URL = os.getenv("WAHA_URL")
Expand Down Expand Up @@ -347,5 +349,16 @@
"name": "trello_server",
"url": os.getenv("TRELLO_MCP_SERVER_URL", "http://localhost:9025/sse")
}
},
"outlook": {
"display_name": "Outlook",
"description": "Connect to read, send, and manage emails in Outlook. The agent can list emails, read message content, send new emails, reply to messages, and manage folders.",
"auth_type": "oauth",
"icon": "IconMail",
"category": "Communication",
"mcp_server_config": {
"name": "outlook_server",
"url": os.getenv("OUTLOOK_MCP_SERVER_URL", "http://localhost:9027/sse")
}
}
}
16 changes: 16 additions & 0 deletions src/server/main/integrations/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
TRELLO_CLIENT_ID, COMPOSIO_API_KEY,
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, SLACK_CLIENT_ID,
SLACK_CLIENT_SECRET, NOTION_CLIENT_ID, NOTION_CLIENT_SECRET,
OUTLOOK_CLIENT_ID, OUTLOOK_CLIENT_SECRET,
)
from workers.tasks import execute_triggered_task
from workers.proactive.utils import event_pre_filter
Expand Down Expand Up @@ -75,6 +76,8 @@ async def get_integration_sources(user_id: str = Depends(auth_helper.get_current
source_info["client_id"] = TRELLO_CLIENT_ID
elif name == 'discord':
source_info["client_id"] = DISCORD_CLIENT_ID
elif name == 'outlook':
source_info["client_id"] = OUTLOOK_CLIENT_ID

all_sources.append(source_info)

Expand Down Expand Up @@ -200,6 +203,15 @@ async def connect_oauth_integration(
"code": request.code,
"redirect_uri": request.redirect_uri
}
elif service_name == 'outlook':
token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
token_payload = {
"client_id": OUTLOOK_CLIENT_ID,
"client_secret": OUTLOOK_CLIENT_SECRET,
"grant_type": "authorization_code",
"code": request.code,
"redirect_uri": request.redirect_uri
}
else:
raise HTTPException(status_code=400, detail=f"OAuth flow not implemented for {service_name}")

Expand Down Expand Up @@ -241,6 +253,10 @@ async def connect_oauth_integration(
if "access_token" not in token_data:
raise HTTPException(status_code=400, detail=f"Discord OAuth error: {token_data.get('error_description', 'No access token.')}")
creds_to_save = token_data # This includes access_token, refresh_token, and the 'bot' object with bot token
elif service_name == 'outlook':
if "access_token" not in token_data:
raise HTTPException(status_code=400, detail=f"Outlook OAuth error: {token_data.get('error_description', 'No access token.')}")
creds_to_save = token_data # This includes access_token, refresh_token, and expires_in

encrypted_creds = aes_encrypt(json.dumps(creds_to_save))

Expand Down
6 changes: 3 additions & 3 deletions src/server/main/memories/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
logger = logging.getLogger(__name__)

router = APIRouter(
prefix="/api/memories",
prefix="/memories",
tags=["Memories"]
)

Expand All @@ -23,7 +23,7 @@ async def startup_event():
utils._initialize_agents()
utils._initialize_embedding_model()

@router.get("/", summary="Get all memories for a user")
@router.get("", summary="Get all memories for a user")
async def get_all_memories(
user_id: str = Depends(PermissionChecker(required_permissions=["read:memory"]))
):
Expand Down Expand Up @@ -65,7 +65,7 @@ async def get_memory_graph(
logger.error(f"Error generating memory graph for user {user_id}: {e}", exc_info=True)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error generating memory graph.")

@router.post("/", summary="Create a new memory for a user")
@router.post("", summary="Create a new memory for a user")
async def create_memory(
request: CreateMemoryRequest,
user_id_and_plan: tuple = Depends(auth_helper.get_current_user_id_and_plan)
Expand Down
134 changes: 134 additions & 0 deletions src/server/mcp_hub/outlook/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Outlook Integration for Sentient

This module provides Outlook email integration for the Sentient AI assistant using Microsoft Graph API.

## Features

- **Read Emails**: List and read emails from different folders (Inbox, Sent Items, etc.)
- **Send Emails**: Compose and send new emails
- **Reply to Emails**: Reply to existing email threads
- **Search Emails**: Search for specific emails using Microsoft Graph search
- **Manage Folders**: List and navigate email folders
- **Privacy Filters**: Apply user-defined privacy filters to email content

## Setup

### 1. Microsoft Azure App Registration

1. Go to [Azure Portal](https://portal.azure.com)
2. Navigate to "Azure Active Directory" > "App registrations"
3. Click "New registration"
4. Fill in the details:
- **Name**: Sentient Outlook Integration
- **Supported account types**: Accounts in any organizational directory and personal Microsoft accounts
- **Redirect URI**: Web - `https://your-domain.com/integrations/oauth/callback`

### 2. Configure API Permissions

1. In your app registration, go to "API permissions"
2. Click "Add a permission"
3. Select "Microsoft Graph"
4. Choose "Delegated permissions"
5. Add the following permissions:
- `Mail.Read` - Read user mail
- `Mail.Send` - Send mail as a user
- `User.Read` - Sign in and read user profile

### 3. Environment Variables

Add the following environment variables to your `.env` file:

```bash
# Outlook OAuth Configuration
OUTLOOK_CLIENT_ID=your_azure_app_client_id
OUTLOOK_CLIENT_SECRET=your_azure_app_client_secret

# Outlook MCP Server URL (optional, defaults to localhost:9027)
OUTLOOK_MCP_SERVER_URL=http://localhost:9027/sse
```

### 4. Start the Outlook MCP Server

```bash
cd src/server/mcp_hub/outlook
python main.py
```

The server will start on port 9027 by default.

## Usage

### Available Tools

1. **get_emails**: Retrieve emails from a specific folder
- Parameters: `folder`, `top`, `skip`, `search`

2. **get_email**: Get a specific email by ID
- Parameters: `message_id`

3. **send_email**: Send a new email
- Parameters: `subject`, `body`, `to_recipients`, `cc_recipients`, `bcc_recipients`

4. **reply_to_email**: Reply to an existing email
- Parameters: `message_id`, `body`, `cc_recipients`, `bcc_recipients`

5. **get_folders**: List email folders
- Parameters: None

6. **search_emails**: Search for emails
- Parameters: `query`, `top`

### Example Usage

```python
# Get recent emails from inbox
emails = await get_emails(folder="inbox", top=10)

# Send an email
result = await send_email(
subject="Test Email",
body="<p>This is a test email.</p>",
to_recipients=["[email protected]"]
)

# Search for emails
search_results = await search_emails(query="meeting", top=5)
```

## Privacy and Security

- All credentials are encrypted using AES encryption
- User privacy filters are applied to email content
- Access tokens are stored securely in MongoDB
- The integration respects Microsoft's data handling policies

## Troubleshooting

### Common Issues

1. **OAuth Error**: Ensure your redirect URI matches exactly in Azure app registration
2. **Permission Denied**: Verify all required API permissions are granted
3. **Token Expired**: The integration handles token refresh automatically
4. **Connection Issues**: Check that the MCP server is running on the correct port

### Debug Mode

Enable debug logging by setting the environment variable:
```bash
ENVIRONMENT=dev-local
```

## API Reference

The integration uses Microsoft Graph API v1.0. For detailed API documentation, visit:
https://docs.microsoft.com/en-us/graph/api/overview

## Contributing

When contributing to this integration:

1. Follow the existing code patterns
2. Add appropriate error handling
3. Include privacy filter considerations
4. Update this README with any new features
5. Test thoroughly with different email scenarios
Loading
Loading