feat: Add automatic webhook setup and Connect Calendar UI for Meeting Scheduler#893
feat: Add automatic webhook setup and Connect Calendar UI for Meeting Scheduler#893salja03-t21 wants to merge 32 commits intoelie222:mainfrom
Conversation
…dling
Fix calendar OAuth callbacks to redirect to /{emailAccountId}/calendars
and standardize error handling between Google and Microsoft providers.
Implement complete Outlook integration for the deep-clean feature: Backend Changes: - Created /api/clean/outlook/route.ts with Graph API integration - Implemented archive operation (moves to Archive folder) - Implemented mark as read operation - Added provider detection and routing in /api/clean/route.ts - Provider-agnostic static rules (starred/flagged, sent, etc.) - QStash integration with signature verification UI Improvements: - Removed premium banner from intro step - Updated 'Starred emails' to 'Starred/Flagged emails' - Provider-aware confirmation text (Gmail vs Outlook) - Dynamic messaging for archive and mark-as-read operations Development Changes: - Commented out premium checks for testing - QStash credentials configured - All required environment variables set Files Created: - apps/web/app/api/clean/outlook/route.ts Files Modified: - apps/web/app/api/clean/route.ts - apps/web/app/(app)/[emailAccountId]/clean/IntroStep.tsx - apps/web/app/(app)/[emailAccountId]/clean/CleanInstructionsStep.tsx - apps/web/app/(app)/[emailAccountId]/clean/ConfirmationStep.tsx - apps/web/.env Note: Premium checks are disabled for development/testing only. Re-enable before production deployment.
- Added onPrevious() function to useStep hook - Added Back buttons to all wizard steps: * ActionSelectionStep * CleanInstructionsStep * TimeRangeStep * ConfirmationStep - Users can now navigate back through the wizard steps - Improves UX by allowing users to correct mistakes without restarting
Temporarily commented out PremiumExpiredCard component in SideNav to allow testing of Outlook deep-clean feature without premium restrictions. This should be re-enabled before production deployment.
Temporarily disable premium banner by making PremiumAlertWithData return null. Original code preserved in comment block for re-enabling before production. This resolves the persistent premium banner issue in the deep-clean wizard.
Temporarily comment out premium validation (lines 41-43) to allow testing the deep-clean wizard without premium subscription. Original code preserved in comments for re-enabling before production.
- Both lines 87 and 153 now correctly access provider from account object - Pattern matches usage throughout codebase (safe-action.ts, etc) - Type definition includes provider in account select
- Refactored undoCleanInboxAction and changeKeepToDoneAction to use provider-agnostic EmailProvider pattern instead of Gmail-only functions - Fixed Undo button to work with both Gmail and Outlook accounts - Hidden 'Process Only These 50 Emails' button on preview page as requested - Integrated useEmailStream hook to properly display emails in real-time via SSE - Fixed React hydration errors in Radix UI components (DropdownMenu, Tooltip) by adding suppressHydrationWarning - Fixed TypeScript error in LoadingContent by changing error prop from string | null to string | undefined
- Add loading state to ConfirmationStep to prevent duplicate submissions - Clamp onPrevious in useStep to prevent underflow below step 0 - Extract duplicate payload construction in PreviewStep into buildCleanPayload helper - Fix LoadingContent error prop type in PreviewStep
- Updated all cursor rules documentation for consistency - Enhanced Outlook email streaming and preview functionality - Added clean-preview utility for better code organization - Improved error handling in Microsoft email utilities - Refined clean preview batch UI components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This merge brings in comprehensive Outlook/Microsoft Calendar support: - OAuth authentication flow for Microsoft Calendar - Calendar syncing with Microsoft Graph API - Unified availability checking across Google and Microsoft calendars - Provider pattern architecture for multi-provider support - AI integration for meeting time suggestions using Microsoft calendars - Updated UI with "Add Outlook Calendar" button - Token refresh handling for Microsoft Calendar - Documentation updates for setup instructions Key features: - Users can connect multiple calendar providers (Google + Microsoft) - AI drafts check availability across all connected calendars - Clean architecture with provider abstraction - Automatic calendar sync after connection Files added: - utils/outlook/calendar-client.ts - Microsoft Graph API client - utils/calendar/providers/microsoft.ts - OAuth provider - utils/calendar/providers/microsoft-availability.ts - Availability checking - utils/calendar/unified-availability.ts - Multi-provider aggregation - app/api/outlook/calendar/* - OAuth endpoints 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Eduardo Lelis <eduardoleliss@gmail.com>
- Add showPreview prop to ConfirmationStep component type - Update clean-preview.ts to use correct Account token structure - Fix token access: use account.access_token instead of tokens.access_token - Convert Date to timestamp (getTime()) for client functions - Use queryBatchMessages for Gmail to get full message details - Fix message property access: use message.headers instead of message.parsedMessage.headers - Remove unsupported 'after' parameter from Outlook getMessages call All TypeScript errors resolved, build now passes successfully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Support email triggers: 'Schedule:' in subject or '/schedule meeting' in body - AI-powered meeting detail extraction - Calendar availability checking - Teams/Zoom/Google Meet link generation - Native calendar event creation (not .ics files) - Automatic invite sending via Google Calendar/Outlook Calendar APIs - Comprehensive 6-phase implementation plan
- Created detectMeetingTrigger utility to detect meeting scheduling triggers - Supports "Schedule:" in subject line (case-insensitive) - Supports "/schedule meeting" command in email body (case-insensitive) - Works for both sent emails and emails to yourself - Integrated detection into webhook processing (Gmail and Outlook) - Added comprehensive test coverage with 19 passing tests - Lazy-loads logger to avoid env validation issues in tests Trigger patterns: 1. Subject: "Schedule:" (e.g., "Schedule: Team meeting next week") 2. Body: "/schedule meeting" (e.g., "Hi team, /schedule meeting to discuss Q4") Both patterns work for: - Sent emails (outbound messages) - Emails to yourself (inbound from your own address) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Phase 2 of meeting scheduler implementation - Created aiParseMeetingRequest to extract structured meeting data - Parses attendees from To/CC fields and email body - Extracts date/time preferences in natural language - Detects meeting duration (default 60 min) - Generates professional meeting title from content - Extracts agenda/purpose from email body - Detects preferred provider (Teams/Zoom/Google Meet) - Identifies in-person meeting locations - Captures special requests and notes - Detects urgency flags (ASAP, urgent, today) - Integrated parser into webhook processing - Added comprehensive test suite (11 test cases) Features: - Zod schema validation for structured output - Uses createGenerateObject for AI integration - Follows codebase LLM patterns - Scoped logging for debugging - Handles self-reminder emails (emails to yourself) Test coverage: ✓ Simple meeting requests ✓ Teams/Zoom/Google Meet detection ✓ Urgency detection ✓ In-person location extraction ✓ Multiple attendees from CC ✓ Detailed agenda extraction ✓ Requests with no specific times ✓ Special preparation notes ✓ Self-reminder emails 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Phase 3 of meeting scheduler implementation - Created findMeetingAvailability to find available time slots - Leverages existing unified calendar integration (Google + Microsoft) - Parses natural language time preferences (e.g., "next Tuesday at 2pm") - Checks requested times against busy periods from all calendars - Suggests alternative times if requested slots are busy - Integrated into webhook processing pipeline Features: - Natural language parsing for common date/time expressions - Supports: "tomorrow at 2pm", "next Tuesday", "Jan 15 at 10am" - Day-of-week detection (Monday-Sunday) - Intelligent time slot conflict detection - Working hours suggestions (9 AM - 5 PM) - Timezone-aware date handling using TZDate - User timezone detection from calendar connections - Fallback suggestions when no specific times requested Algorithm: 1. Parse user's timezone from connected calendars 2. Parse natural language date/time preferences 3. Fetch busy periods across all Google + Microsoft calendars 4. Check if requested times are available (no conflicts) 5. If conflicts found, suggest alternative times 6. Return structured availability data with timezone Time slot conflict detection: - Checks for overlaps: slot starts before busy ends AND slot ends after busy starts - Returns only available slots (no conflicts) Suggestions algorithm: - Checks next 7 days by default - Prioritizes times near user's preferred hour - Working hours: 9 AM - 5 PM - Returns up to 5 suggestions - Filters duplicate slots 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add meeting link generators for Microsoft Teams and Google Meet with provider validation. **New Files:** - `create-meeting-link.ts`: Main orchestration for creating meeting links with provider validation - `providers/types.ts`: Provider validation logic ensuring Teams=Microsoft only, Google Meet=Google only - `providers/teams.ts`: Microsoft Teams meeting creation via Graph API /me/onlineMeetings - `providers/google-meet.ts`: Google Meet conferenceData generation for calendar events **Updates:** - `parse-meeting-request.ts`: Fixed EmailForLLM type usage (removed .headers accessor) - `process-history-item.ts`: Integrated meeting link creation into webhook flow **Key Features:** - Provider compatibility validation (Teams for Microsoft, Google Meet for Google) - Automatic fallback to native provider when incompatible provider requested - Zoom support deferred (returns fallback to native provider) - Teams uses Microsoft Graph API with OAuth refresh - Google Meet uses conferenceData structure (link auto-created by Google Calendar) Next: Phase 5 (calendar event creation) and Phase 6 (user settings) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add unified calendar event creation for both Google Calendar and Microsoft Calendar with full meeting link integration. **New File:** - `create-calendar-event.ts`: Unified calendar event creation with provider-specific implementations **Key Features:** - **Google Calendar**: Uses googleapis calendar_v3 API - Creates events with conferenceData for Google Meet - Sets sendUpdates='all' to notify attendees - Includes description with agenda, notes, and meeting link - **Microsoft Calendar**: Uses Microsoft Graph API - Creates events with Teams meeting integration - Sets isOnlineMeeting and onlineMeetingProvider - Includes body content with agenda, notes, and meeting link - **Unified Interface**: Single function handles both providers - Automatically detects provider from email account - Gets primary/enabled calendar - Validates authentication tokens - Returns standardized event details (eventId, eventUrl, provider) - **Full Integration**: Connected to meeting details - Adds all attendees from parsed meeting request - Sets location if specified - Includes agenda and notes in event description - Applies correct timezone from availability checker - Sends calendar invitations automatically **Updates:** - `process-history-item.ts`: Integrated calendar event creation after meeting link generation **Flow:** 1. Parse meeting request → 2. Find availability → 3. Generate meeting link → 4. **Create calendar event** → 5. Send confirmation email (TODO) Next: Phase 6 (user settings) and testing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add unit tests for meeting provider validation logic: - Test getAvailableProviders() for both Google and Microsoft accounts - Test validateProviderForAccount() for all provider/account combinations - Verify Teams=Microsoft only, Google Meet=Google only - Test fallback logic for incompatible providers - Test 'none' and 'zoom' edge cases All 11 tests passing ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add user-configurable settings for meeting scheduler: - meetingSchedulerEnabled: Enable/disable automatic meeting scheduling (default: true) - meetingSchedulerDefaultDuration: Default meeting duration in minutes (default: 60) - meetingSchedulerPreferredProvider: Preferred meeting provider (auto/teams/google-meet/zoom/none) - meetingSchedulerWorkingHoursStart: Working hours start 0-23 (default: 9) - meetingSchedulerWorkingHoursEnd: Working hours end 0-23 (default: 17) - meetingSchedulerAutoCreate: Auto-create events or prompt for confirmation (default: true) Migration applied successfully ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add database schema fields for meeting scheduler configuration: - meetingSchedulerEnabled: toggle meeting scheduler on/off - meetingSchedulerDefaultDuration: default meeting duration (15-240 minutes) - meetingSchedulerPreferredProvider: preferred meeting provider (auto, teams, google-meet, zoom, none) - meetingSchedulerWorkingHoursStart/End: working hours for scheduling (0-23) - meetingSchedulerAutoCreate: auto-create meetings without confirmation - Create validation schema with Zod for settings updates - Implement server action for updating meeting scheduler settings with working hours validation - Integrate settings check into webhook processing to respect user preferences - Update webhook type definitions to include meetingSchedulerEnabled - Add meetingSchedulerEnabled to webhook account selection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…idation - Add 24 test cases covering all validation scenarios - Test valid inputs for all settings fields - Test boundary conditions (min/max values) - Test edge cases (0, 23 hours; 15, 240 minutes) - Test invalid inputs (out of range, wrong types) - Test optional field handling - All tests passing (24/24) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Create GET API route for fetching meeting scheduler settings - Build comprehensive settings UI component with: - Enable/disable toggle for meeting scheduler - Default meeting duration input (15-240 minutes) - Preferred meeting provider dropdown (auto, teams, google-meet, zoom, none) - Working hours configuration (start/end hours 0-23) - Auto-create toggle for automatic meeting creation - Dynamic provider options based on account type (Google/Microsoft) - Form validation with React Hook Form and Zod - Integrated with existing settings page under Email Account tab - Real-time form state management with SWR data fetching - Proper error handling with toast notifications 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
… Scheduler Implements automatic webhook configuration with manual fallback UI to enable real-time email processing for the Meeting Scheduler feature. Premium checks temporarily disabled for testing. Key changes: - Auto-setup Outlook webhooks when enabling Meeting Scheduler - Add "Connect Calendar" button with status display in Settings - Disable premium checks in webhook cron job for testing - Fix HTML validation errors in ProgressPanel component - Fix form layout issues in Meeting Scheduler settings - Enable Share Premium section for testing - Update API to return webhook expiration status 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
@salja03-t21 is attempting to deploy a commit to the Inbox Zero OSS Program Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedThe pull request is closed. WalkthroughThis PR introduces a comprehensive Meeting Scheduler feature triggered by email patterns, implements multi-provider email abstraction to support Outlook alongside Gmail, refactors the Deep Clean flow with preview capabilities and back navigation, extends calendar OAuth integration with Microsoft provider support, and adds temporary testing bypasses for premium features. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Email as Email Provider
participant EmailStream as Email Stream
participant DetectTrigger as Detect Trigger
participant MeetingService as Meeting Service
participant Calendar as Calendar Provider
User->>Email: Receives email with "Schedule: meeting"
Email->>EmailStream: Streams email to user
EmailStream->>DetectTrigger: Extract subject/body
DetectTrigger-->>EmailStream: {isTriggered: true, triggerType: "schedule_subject"}
EmailStream->>MeetingService: Parse meeting request via AI
MeetingService-->>MeetingService: Extract attendees, time, provider preference
MeetingService->>Calendar: Create calendar event
Calendar-->>MeetingService: {eventId, eventUrl, provider}
MeetingService->>Email: Optional: Send confirmation/update email
Email-->>User: Meeting created & calendar updated
sequenceDiagram
participant User
participant UI as Deep Clean UI
participant Preview as Preview Step
participant EmailAPI as Email API
participant Clean as Clean Service
User->>UI: Start deep clean flow
UI->>Preview: IntroStep → TimeRangeStep → ... → PreviewStep
Preview->>EmailAPI: Fetch preview batch (limited count)
EmailAPI-->>Preview: Sample emails matching criteria
Preview->>UI: Render sandbox results with "Process Preview" & "Full Inbox" buttons
User->>UI: Choose "Process Preview Only"
UI->>Clean: Run cleanup on preview batch
Clean-->>UI: Success with preview mode enabled
UI->>UI: Show results, enable "Run on Full Inbox" button
User->>UI: Click "Run on Full Inbox"
UI->>Clean: Execute full cleanup
Clean-->>UI: Completion
sequenceDiagram
participant Browser as Browser
participant AuthURL as Auth URL Endpoint
participant OAuth as OAuth Provider
participant Callback as Callback Endpoint
participant DB as Database
participant UI as Calendar UI
Browser->>AuthURL: Request calendar OAuth URL
AuthURL->>AuthURL: Create state (emailAccountId + nonce)
AuthURL-->>Browser: Return OAuth URL + set state cookie
Browser->>OAuth: Redirect to OAuth provider (Google/Microsoft)
OAuth-->>Browser: Prompt user for consent
Browser->>Callback: Redirect with authorization code
Callback->>Callback: Validate state cookie + extract emailAccountId
Callback->>OAuth: Exchange code for tokens
OAuth-->>Callback: Return access/refresh tokens
Callback->>DB: Create calendar connection + sync calendars
DB-->>Callback: Connection stored
Callback-->>Browser: Redirect to calendars page with success message
UI->>UI: Display connected calendar(s)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Areas requiring extra attention:
Possibly related PRs
Suggested reviewers
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (107)
⛔ Files not processed due to max files limit (15)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| const storedState = request.cookies.get(CALENDAR_STATE_COOKIE_NAME)?.value; | ||
|
|
||
| const redirectUrl = new URL("/calendars", request.nextUrl.origin); | ||
| const response = NextResponse.redirect(redirectUrl); |
There was a problem hiding this comment.
Security control: Static Code Analysis Js
Open Redirect Vulnerability In Express Redirect() Method
Untrusted user input in redirect() can result in Open Redirect vulnerability.
Severity: HIGH
Jit Bot commands and options (e.g., ignore issue)
You can trigger Jit actions by commenting on this PR review:
#jit_ignore_fpIgnore and mark this specific single instance of finding as “False Positive”#jit_ignore_acceptIgnore and mark this specific single instance of finding as “Accept Risk”#jit_ignore_type_in_fileIgnore any finding of type "Open Redirect Vulnerability in Express redirect() Method" in apps/web/utils/calendar/oauth-callback-helpers.ts; future occurrences will also be ignored.#jit_undo_ignoreUndo ignore command
There was a problem hiding this comment.
14 issues found across 123 files
Prompt for AI agents (all 14 issues)
Understand the root cause of the following 14 issues and fix them.
<file name="apps/web/app/api/outlook/watch/all/route.ts">
<violation number="1" location="apps/web/app/api/outlook/watch/all/route.ts:20">
By commenting out the premium checks, this cron will now create Outlook watch subscriptions for every Microsoft-connected account, including non-premium users. That revokes the intended paywall, potentially flooding Outlook with subscriptions and incurring unexpected costs/limits. Please keep the premium gating in place or guard the bypass behind a non-production flag.</violation>
</file>
<file name="apps/web/utils/email/microsoft.ts">
<violation number="1" location="apps/web/utils/email/microsoft.ts:1110">
Returning an empty result here masks provider failures, so callers like /api/threads now respond 200 with zero threads instead of surfacing the underlying error. Please propagate the error (e.g., rethrow after logging) so upstream handlers can return the appropriate failure response.</violation>
</file>
<file name="apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx">
<violation number="1" location="apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx:39">
Hard-coding `hasAiAccess` to `true` removes the premium gate for bulk AI processing, letting non-premium users run this costly feature. Please restore the `usePremium()` check instead of bypassing it.</violation>
</file>
<file name="apps/web/utils/premium/index.ts">
<violation number="1" location="apps/web/utils/premium/index.ts:19">
Returning true here bypasses the real Stripe/Lemon Squeezy premium checks, so every user is treated as premium and gains paid features without a subscription.</violation>
</file>
<file name="apps/web/utils/actions/clean-preview.ts">
<violation number="1" location="apps/web/utils/actions/clean-preview.ts:84">
The Outlook preview path ignores the `daysOld` filter, so Microsoft users will see messages outside the intended window; the date constraint needs to be applied when fetching Outlook messages.</violation>
</file>
<file name=".cursor/rules/features/digest.mdc">
<violation number="1" location=".cursor/rules/features/digest.mdc:2">
Removing the closing ``` leaves the final TypeScript code block unterminated, which breaks Markdown rendering for the remainder of the document.</violation>
</file>
<file name="apps/web/utils/webhook/process-history-item.ts">
<violation number="1" location="apps/web/utils/webhook/process-history-item.ts:168">
This code calls the AI parser before we verify hasAiAccess. Accounts without AI access (but with meetingSchedulerEnabled) will still invoke aiParseMeetingRequest and fail every time a trigger fires. Please gate the meeting-scheduler flow behind the existing hasAiAccess check.</violation>
</file>
<file name="apps/web/app/api/clean/outlook/route.ts">
<violation number="1" location="apps/web/app/api/clean/outlook/route.ts:77">
This retrieves only the first page of conversation messages from Microsoft Graph, so longer threads keep their later messages untouched. Please loop through @odata.nextLink to process the entire conversation.</violation>
</file>
<file name="apps/web/utils/meetings/find-availability.ts">
<violation number="1" location="apps/web/utils/meetings/find-availability.ts:263">
Past requested times are being returned as available. Please skip any slot whose start is before the current time so we don’t suggest already-expired windows to the user.</violation>
</file>
<file name="apps/web/utils/meetings/detect-meeting-trigger.ts">
<violation number="1" location="apps/web/utils/meetings/detect-meeting-trigger.ts:89">
HTML meeting-trigger detection should normalize the markup before running the regex; otherwise formatted emails like `<p>/schedule <strong>meeting</strong></p>` will never trigger the scheduler.</violation>
</file>
<file name="apps/web/prisma/schema.prisma">
<violation number="1" location="apps/web/prisma/schema.prisma:134">
Defaulting `meetingSchedulerEnabled` to true causes every account to appear enabled and skips the false→true transition that provisions the Outlook webhook, so first-time enabling never auto-configures the webhook. Please default this flag to false.</violation>
</file>
<file name="MEETING_SCHEDULER_PLAN.md">
<violation number="1" location="MEETING_SCHEDULER_PLAN.md:307">
Setting `identity.user.id` to the attendee email will make the Graph /me/onlineMeetings call fail because that field must be the Azure AD object id or use the `upn` property for email addresses.</violation>
<violation number="2" location="MEETING_SCHEDULER_PLAN.md:631">
`sendEmail` is given `emailAccountId`, which is an internal id rather than the organizer's email address, so the notification will never reach the user.</violation>
</file>
<file name="apps/web/app/(app)/[emailAccountId]/clean/useEmailStream.ts">
<violation number="1" location="apps/web/app/(app)/[emailAccountId]/clean/useEmailStream.ts:141">
The delayed SSE cleanup keeps the previous EventSource alive when the component remounts quickly (e.g., after `emailAccountId` changes), leaving `eventSourceRef.current` populated so the next `connectToSSE()` aborts. This prevents establishing a stream for the new account and leaves the UI stuck on the old data.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| ], | ||
| }, | ||
| }, | ||
| // TEMPORARILY DISABLED FOR TESTING - All Microsoft accounts included |
There was a problem hiding this comment.
By commenting out the premium checks, this cron will now create Outlook watch subscriptions for every Microsoft-connected account, including non-premium users. That revokes the intended paywall, potentially flooding Outlook with subscriptions and incurring unexpected costs/limits. Please keep the premium gating in place or guard the bypass behind a non-production flag.
Prompt for AI agents
Address the following comment on apps/web/app/api/outlook/watch/all/route.ts at line 20:
<comment>By commenting out the premium checks, this cron will now create Outlook watch subscriptions for every Microsoft-connected account, including non-premium users. That revokes the intended paywall, potentially flooding Outlook with subscriptions and incurring unexpected costs/limits. Please keep the premium gating in place or guard the bypass behind a non-production flag.</comment>
<file context>
@@ -18,14 +17,15 @@ async function watchAllEmails() {
- ],
- },
- },
+ // TEMPORARILY DISABLED FOR TESTING - All Microsoft accounts included
+ // user: {
+ // premium: {
</file context>
| const toAddress = | ||
| message.toRecipients?.[0]?.emailAddress?.address || ""; | ||
|
|
||
| return { |
There was a problem hiding this comment.
Returning an empty result here masks provider failures, so callers like /api/threads now respond 200 with zero threads instead of surfacing the underlying error. Please propagate the error (e.g., rethrow after logging) so upstream handlers can return the appropriate failure response.
Prompt for AI agents
Address the following comment on apps/web/utils/email/microsoft.ts at line 1110:
<comment>Returning an empty result here masks provider failures, so callers like /api/threads now respond 200 with zero threads instead of surfacing the underlying error. Please propagate the error (e.g., rethrow after logging) so upstream handlers can return the appropriate failure response.</comment>
<file context>
@@ -960,188 +960,204 @@ export class OutlookProvider implements EmailProvider {
+ const toAddress =
+ message.toRecipients?.[0]?.emailAddress?.address || "";
+
+ return {
+ id: message.id || "",
+ threadId: message.conversationId || "",
</file context>
|
|
||
| const { hasAiAccess, isLoading: isLoadingPremium } = usePremium(); | ||
| // Temporarily disable premium check for testing | ||
| const hasAiAccess = true; |
There was a problem hiding this comment.
Hard-coding hasAiAccess to true removes the premium gate for bulk AI processing, letting non-premium users run this costly feature. Please restore the usePremium() check instead of bypassing it.
Prompt for AI agents
Address the following comment on apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx at line 39:
<comment>Hard-coding `hasAiAccess` to `true` removes the premium gate for bulk AI processing, letting non-premium users run this costly feature. Please restore the `usePremium()` check instead of bypassing it.</comment>
<file context>
@@ -35,7 +35,10 @@ export function BulkRunRules() {
- const { hasAiAccess, isLoading: isLoadingPremium } = usePremium();
+ // Temporarily disable premium check for testing
+ const hasAiAccess = true;
+ const isLoadingPremium = false;
+ // const { hasAiAccess, isLoading: isLoadingPremium } = usePremium();
</file context>
| isPremiumLemonSqueezy(lemonSqueezyRenewsAt) | ||
| ); | ||
| // Premium enabled for all users permanently | ||
| return true; |
There was a problem hiding this comment.
Returning true here bypasses the real Stripe/Lemon Squeezy premium checks, so every user is treated as premium and gains paid features without a subscription.
Prompt for AI agents
Address the following comment on apps/web/utils/premium/index.ts at line 19:
<comment>Returning true here bypasses the real Stripe/Lemon Squeezy premium checks, so every user is treated as premium and gains paid features without a subscription.</comment>
<file context>
@@ -15,10 +15,12 @@ export const isPremium = (
- isPremiumLemonSqueezy(lemonSqueezyRenewsAt)
- );
+ // Premium enabled for all users permanently
+ return true;
+ // return (
+ // isPremiumStripe(stripeSubscriptionStatus) ||
</file context>
| return true; | |
| return isPremiumStripe(stripeSubscriptionStatus) || isPremiumLemonSqueezy(lemonSqueezyRenewsAt); |
| emailAccountId: emailAccount.id, | ||
| }); | ||
|
|
||
| const { messages } = await getOutlookMessages(outlook, { |
There was a problem hiding this comment.
The Outlook preview path ignores the daysOld filter, so Microsoft users will see messages outside the intended window; the date constraint needs to be applied when fetching Outlook messages.
Prompt for AI agents
Address the following comment on apps/web/utils/actions/clean-preview.ts at line 84:
<comment>The Outlook preview path ignores the `daysOld` filter, so Microsoft users will see messages outside the intended window; the date constraint needs to be applied when fetching Outlook messages.</comment>
<file context>
@@ -0,0 +1,105 @@
+ emailAccountId: emailAccount.id,
+ });
+
+ const { messages } = await getOutlookMessages(outlook, {
+ query: "",
+ maxResults: PREVIEW_RUN_COUNT,
</file context>
| : false; | ||
|
|
||
| const hasScheduleInHtmlBody = htmlBody | ||
| ? scheduleCommandPattern.test(htmlBody) |
There was a problem hiding this comment.
HTML meeting-trigger detection should normalize the markup before running the regex; otherwise formatted emails like <p>/schedule <strong>meeting</strong></p> will never trigger the scheduler.
Prompt for AI agents
Address the following comment on apps/web/utils/meetings/detect-meeting-trigger.ts at line 89:
<comment>HTML meeting-trigger detection should normalize the markup before running the regex; otherwise formatted emails like `<p>/schedule <strong>meeting</strong></p>` will never trigger the scheduler.</comment>
<file context>
@@ -0,0 +1,143 @@
+ : false;
+
+ const hasScheduleInHtmlBody = htmlBody
+ ? scheduleCommandPattern.test(htmlBody)
+ : false;
+
</file context>
| multiRuleSelectionEnabled Boolean @default(false) | ||
|
|
||
| // Meeting Scheduler settings | ||
| meetingSchedulerEnabled Boolean @default(true) // Enable/disable automatic meeting scheduling |
There was a problem hiding this comment.
Defaulting meetingSchedulerEnabled to true causes every account to appear enabled and skips the false→true transition that provisions the Outlook webhook, so first-time enabling never auto-configures the webhook. Please default this flag to false.
Prompt for AI agents
Address the following comment on apps/web/prisma/schema.prisma at line 134:
<comment>Defaulting `meetingSchedulerEnabled` to true causes every account to appear enabled and skips the false→true transition that provisions the Outlook webhook, so first-time enabling never auto-configures the webhook. Please default this flag to false.</comment>
<file context>
@@ -130,6 +130,14 @@ model EmailAccount {
multiRuleSelectionEnabled Boolean @default(false)
+ // Meeting Scheduler settings
+ meetingSchedulerEnabled Boolean @default(true) // Enable/disable automatic meeting scheduling
+ meetingSchedulerDefaultDuration Int @default(60) // Default meeting duration in minutes (30, 60, 90)
+ meetingSchedulerPreferredProvider String? // Preferred meeting provider: 'auto', 'teams', 'google-meet', 'zoom', 'none'
</file context>
| meetingSchedulerEnabled Boolean @default(true) // Enable/disable automatic meeting scheduling | |
| meetingSchedulerEnabled Boolean @default(false) // Enable/disable automatic meeting scheduling |
|
|
||
| // Send notification email to organizer | ||
| await sendEmail({ | ||
| to: [emailAccountId], // Send to self |
There was a problem hiding this comment.
sendEmail is given emailAccountId, which is an internal id rather than the organizer's email address, so the notification will never reach the user.
Prompt for AI agents
Address the following comment on MEETING_SCHEDULER_PLAN.md at line 631:
<comment>`sendEmail` is given `emailAccountId`, which is an internal id rather than the organizer's email address, so the notification will never reach the user.</comment>
<file context>
@@ -0,0 +1,1020 @@
+
+ // Send notification email to organizer
+ await sendEmail({
+ to: [emailAccountId], // Send to self
+ subject: `Meeting Scheduled: ${eventDetails.title}`,
+ body: emailBody,
</file context>
| attendees: participants.map(email => ({ | ||
| identity: { | ||
| user: { | ||
| id: email |
There was a problem hiding this comment.
Setting identity.user.id to the attendee email will make the Graph /me/onlineMeetings call fail because that field must be the Azure AD object id or use the upn property for email addresses.
Prompt for AI agents
Address the following comment on MEETING_SCHEDULER_PLAN.md at line 307:
<comment>Setting `identity.user.id` to the attendee email will make the Graph /me/onlineMeetings call fail because that field must be the Azure AD object id or use the `upn` property for email addresses.</comment>
<file context>
@@ -0,0 +1,1020 @@
+ attendees: participants.map(email => ({
+ identity: {
+ user: {
+ id: email
+ }
+ }
</file context>
| } | ||
| isMountedRef.current = false; | ||
| // Don't close connection immediately - wait to see if component remounts | ||
| setTimeout(() => { |
There was a problem hiding this comment.
The delayed SSE cleanup keeps the previous EventSource alive when the component remounts quickly (e.g., after emailAccountId changes), leaving eventSourceRef.current populated so the next connectToSSE() aborts. This prevents establishing a stream for the new account and leaves the UI stuck on the old data.
Prompt for AI agents
Address the following comment on apps/web/app/(app)/[emailAccountId]/clean/useEmailStream.ts at line 141:
<comment>The delayed SSE cleanup keeps the previous EventSource alive when the component remounts quickly (e.g., after `emailAccountId` changes), leaving `eventSourceRef.current` populated so the next `connectToSSE()` aborts. This prevents establishing a stream for the new account and leaves the UI stuck on the old data.</comment>
<file context>
@@ -89,38 +100,58 @@ export function useEmailStream(
- }
+ isMountedRef.current = false;
+ // Don't close connection immediately - wait to see if component remounts
+ setTimeout(() => {
+ if (!isMountedRef.current) {
+ console.log("Cleaning up SSE connection (component unmounted)");
</file context>
Summary
Implements automatic webhook configuration with manual fallback UI to enable real-time email processing for the Meeting Scheduler feature. Premium checks temporarily disabled for testing.
Key Changes
ProgressPanelcomponent (p>div nesting issue)showPreviewprop from clean onboarding pagewatchEmailsExpirationDateandaccount.providerto Meeting Scheduler settings endpointTesting
See
MEETING_SCHEDULER_README.mdfor comprehensive testing guide.Production Checklist
Before deploying to production:
/api/outlook/watch/all/route.tsMultiAccountSection.tsx🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements
Documentation