Skip to content
Merged
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
2 changes: 1 addition & 1 deletion apps/web/__tests__/ai/choose-rule/draft-management.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import prisma from "@/utils/prisma";
import { ActionType } from "@prisma/client";
import { createScopedLogger } from "@/utils/logger";
import type { ParsedMessage } from "@/utils/types";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

vi.mock("@/utils/prisma", () => ({
default: {
Expand Down
8 changes: 6 additions & 2 deletions apps/web/__tests__/ai/reply/reply-context-collector.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, test, vi } from "vitest";
import { afterEach, describe, expect, test, vi } from "vitest";
import { aiCollectReplyContext } from "@/utils/ai/reply/reply-context-collector";
import type { EmailForLLM, ParsedMessage } from "@/utils/types";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import { getEmailAccount } from "@/__tests__/helpers";

// Run with: pnpm test-ai reply-context-collector
Expand All @@ -12,6 +12,10 @@ const isAiTest = process.env.RUN_AI_TESTS === "true";
const TEST_TIMEOUT = 60_000;

describe.runIf(isAiTest)("aiCollectReplyContext", () => {
afterEach(() => {
vi.clearAllMocks();
});

test(
"collects historical context and returns relevant emails",
async () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/messages/batch/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
import { withEmailProvider } from "@/utils/middleware";
import { messagesBatchQuery } from "@/app/api/messages/validation";
import { parseReply } from "@/utils/mail";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

export type MessagesBatchResponse = {
messages: Awaited<ReturnType<typeof getMessagesBatch>>;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/messages/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { messageQuerySchema } from "@/app/api/messages/validation";
import { createScopedLogger } from "@/utils/logger";
import { isAssistantEmail } from "@/utils/assistant/is-assistant-email";
import { GmailLabel } from "@/utils/gmail/label";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

const logger = createScopedLogger("api/messages");

Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/outlook/webhook/process-history-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from "@/utils/reply-tracker/draft-tracking";
import { formatError } from "@/utils/error";
import { createEmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

export async function processHistoryItem(
resourceData: OutlookResourceData,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/threads/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { z } from "zod";
import { NextResponse } from "next/server";
import { withEmailProvider } from "@/utils/middleware";
import { createScopedLogger } from "@/utils/logger";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

const threadQuery = z.object({ id: z.string() });
export type ThreadQuery = z.infer<typeof threadQuery>;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/threads/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getCategory } from "@/utils/redis/category";
import { ExecutedRuleStatus } from "@prisma/client";
import { createScopedLogger } from "@/utils/logger";
import { isIgnoredSender } from "@/utils/filter-ignored-senders";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

LGTM: Updated type import aligns with modularized email provider types

Importing EmailProvider from "@/utils/email/types" (type-only) is correct and consistent.

Run this to verify no lingering imports from the old path remain:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Find any remaining imports from the old provider path across the repo
rg -n $'from\\s+[\'"]@/utils/email/provider[\'"]' -S || echo "No stale imports found ✔️"

# Optional: ensure all imports use type-only where appropriate
rg -n $'import\\s+\\{?\\s*EmailProvider\\s*\\}?\\s+from\\s+[\'"]@/utils/email/types[\'"]' -S

Length of output: 2468


Fix stale EmailProvider type imports

The repository still contains type-only imports of EmailProvider from the old provider path. Please update them to the new types location. For example:

• apps/web/app/api/user/stats/newsletters/helpers.ts (line 1)

- import type { EmailProvider, EmailFilter } from "@/utils/email/provider";
+ import type { EmailProvider, EmailFilter } from "@/utils/email/types";

After applying this, re-run your grep command to ensure no remaining imports from "@/utils/email/provider" reference EmailProvider.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/web/app/api/threads/route.ts at line 10, the import of the EmailProvider
type is from the outdated path "@/utils/email/types". Update this import to the
new correct path where EmailProvider is currently defined, matching the updated
project structure. After making this change, run a grep search to confirm no
other imports of EmailProvider remain from the old provider path
"@/utils/email/provider".


const logger = createScopedLogger("api/threads");

Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/user/stats/newsletters/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EmailProvider, EmailFilter } from "@/utils/email/provider";
import type { EmailProvider, EmailFilter } from "@/utils/email/types";
import { extractEmailAddress } from "@/utils/email";
import prisma from "@/utils/prisma";
import { NewsletterStatus } from "@prisma/client";
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/user/stats/newsletters/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createScopedLogger } from "@/utils/logger";
import prisma from "@/utils/prisma";
import { Prisma } from "@prisma/client";
import { z } from "zod";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import {
getAutoArchiveFilters,
findNewsletterStatus,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/watch/controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import prisma from "@/utils/prisma";
import { captureException } from "@/utils/error";
import { createScopedLogger } from "@/utils/logger";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

const logger = createScopedLogger("watch/controller");

Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/ai/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { callWebhook } from "@/utils/webhook";
import type { ActionItem, EmailForAction } from "@/utils/ai/types";
import { coordinateReplyProcess } from "@/utils/reply-tracker/inbound";
import { internalDateToDate } from "@/utils/date";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import { enqueueDigestItem } from "@/utils/digest/index";

const logger = createScopedLogger("ai-actions");
Expand Down
18 changes: 16 additions & 2 deletions apps/web/utils/ai/choose-rule/choose-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { fetchMessagesAndGenerateDraft } from "@/utils/reply-tracker/generate-dr
import { getEmailForLLM } from "@/utils/get-email-from-message";
import { aiGenerateArgs } from "@/utils/ai/choose-rule/ai-choose-args";
import { createScopedLogger } from "@/utils/logger";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

const logger = createScopedLogger("choose-args");

Expand Down Expand Up @@ -46,13 +46,27 @@ export async function getActionItemsWithAiArgs({

if (draftEmailActions.length) {
try {
logger.info("Generating draft", {
email: emailAccount.email,
threadId: message.threadId,
});

draft = await fetchMessagesAndGenerateDraft(
emailAccount,
message.threadId,
client,
);

logger.info("Draft generated", {
email: emailAccount.email,
threadId: message.threadId,
});
} catch (error) {
logger.error("Failed to generate draft", { error });
logger.error("Failed to generate draft", {
email: emailAccount.email,
threadId: message.threadId,
error,
});
// Continue without draft if generation fails
draft = null;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/ai/choose-rule/draft-management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import prisma from "@/utils/prisma";
import { ActionType } from "@prisma/client";
import type { ExecutedRule } from "@prisma/client";
import type { Logger } from "@/utils/logger";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

/**
* Handles finding and potentially deleting a previous AI-generated draft for a thread.
Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/ai/choose-rule/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ExecutedRuleStatus, ActionType } from "@prisma/client";
import { createScopedLogger } from "@/utils/logger";
import type { ParsedMessage } from "@/utils/types";
import { updateExecutedActionWithDraftId } from "@/utils/ai/choose-rule/draft-management";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

type ExecutedRuleWithActionItems = Prisma.ExecutedRuleGetPayload<{
include: { actionItems: true };
Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/ai/choose-rule/match-rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import type {
ParsedMessage,
ParsedMessageHeaders,
} from "@/utils/types";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import prisma from "@/utils/__mocks__/prisma";
import { aiChooseRule } from "@/utils/ai/choose-rule/ai-choose-rule";
import { getEmailAccount } from "@/__tests__/helpers";
Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/ai/choose-rule/match-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import type {
import { extractEmailAddress } from "@/utils/email";
import { hasIcsAttachment } from "@/utils/parse/calender-event";
import { checkSenderReplyHistory } from "@/utils/reply-tracker/check-sender-reply-history";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import type { ModelType } from "@/utils/llms/model";

const logger = createScopedLogger("match-rules");
Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/ai/choose-rule/run-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
cancelScheduledActions,
} from "@/utils/scheduled-actions/scheduler";
import groupBy from "lodash/groupBy";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import type { ModelType } from "@/utils/llms/model";

const logger = createScopedLogger("ai-run-rules");
Expand Down
19 changes: 14 additions & 5 deletions apps/web/utils/ai/reply/reply-context-collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { EmailForLLM } from "@/utils/types";
import { stringifyEmail } from "@/utils/stringify-email";
import { getTodayForLLM } from "@/utils/llms/helpers";
import { getModel } from "@/utils/llms/model";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import { getEmailForLLM } from "@/utils/get-email-from-message";
import { captureException } from "@/utils/error";

Expand All @@ -22,7 +22,7 @@ const resultSchema = z.object({
relevantEmails: z
.array(z.string())
.describe(
"Relevant emails and the user's responses. One question+answer per array item.",
"Past email conversations from search results that could help draft the response. Leave empty if no relevant past emails found.",
),
});
export type ReplyContextCollectorResult = z.infer<typeof resultSchema>;
Expand All @@ -32,14 +32,17 @@ const agentSystem = `You are an intelligent email assistant that gathers histori
Your task is to:
1. Analyze the current email thread to understand the main topic, question, or request
2. Search through the user's email history to find similar conversations from the past 6 months
3. Collect and synthesize the most relevant findings
3. Collect and synthesize the most relevant findings from your searches
4. When you are done, CALL finalizeResults with your final results

You have access to these tools:
- searchEmails: Search for emails using queries to find relevant historical context
- finalizeResults: Finalize and return your results

Important guidelines:
CRITICAL GUIDELINES:
- The current email thread is already provided to the drafting agent - DO NOT include it in relevantEmails
- The relevantEmails array should ONLY contain past emails found through your searches that could help draft a response
- If no relevant past emails are found through searching, leave the relevantEmails array empty
- Perform as many searches as needed to confidently gather context, but be efficient
- Focus on emails that show how similar questions were answered before
- Only include information that directly helps a downstream drafting agent
Expand Down Expand Up @@ -126,6 +129,9 @@ ${getTodayForLLM()}`;
return getEmailForLLM(message, { maxLength: 2000 });
});

logger.info("Found emails", { emails: emails.length });
// logger.trace("Found emails", { emails });

return emails;
} catch (error) {
const errorMessage =
Expand All @@ -148,9 +154,12 @@ ${getTodayForLLM()}`;
inputSchema: resultSchema,
execute: async (finalResult) => {
logger.info("Finalizing results", {
notes: finalResult.notes,
relevantEmails: finalResult.relevantEmails.length,
});
logger.trace("Finalizing results", {
notes: finalResult.notes,
relevantEmails: finalResult.relevantEmails,
});

result = finalResult;

Expand Down
4 changes: 2 additions & 2 deletions apps/web/utils/assess.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import uniq from "lodash/uniq";
import countBy from "lodash/countBy";
import type { EmailProvider } from "@/utils/email/provider";
import { GmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import { GmailProvider } from "@/utils/email/google";
import { getEmailClient } from "@/utils/mail";
import { isDefined } from "@/utils/types";
import { createScopedLogger } from "@/utils/logger";
Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/assistant/process-assistant-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import prisma from "@/utils/prisma";
import { emailToContent } from "@/utils/mail";
import { isAssistantEmail } from "@/utils/assistant/is-assistant-email";
import { internalDateToDate } from "@/utils/date";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

const logger = createScopedLogger("process-assistant-email");

Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/categorize/senders/categorize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { EmailAccountWithAI } from "@/utils/llms/types";
import { createScopedLogger } from "@/utils/logger";
import { extractEmailAddress } from "@/utils/email";
import { SafeError } from "@/utils/error";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

const logger = createScopedLogger("categorize/senders");

Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/cold-email/is-cold-email.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import prisma from "@/utils/prisma";
import { ColdEmailSetting, ColdEmailStatus } from "@prisma/client";
import { blockColdEmailWithProvider } from "./is-cold-email";
import { getEmailAccount } from "@/__tests__/helpers";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";

// Mock dependencies
vi.mock("server-only", () => ({}));
Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/cold-email/is-cold-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { DEFAULT_COLD_EMAIL_PROMPT } from "@/utils/cold-email/prompt";
import { stringifyEmail } from "@/utils/stringify-email";
import { createScopedLogger } from "@/utils/logger";
import type { EmailForLLM } from "@/utils/types";
import type { EmailProvider } from "@/utils/email/provider";
import type { EmailProvider } from "@/utils/email/types";
import { getModel, type ModelType } from "@/utils/llms/model";
import { createGenerateObject } from "@/utils/llms";

Expand Down
Loading
Loading