Skip to content

Commit 732e67d

Browse files
committed
Merge branch 'main' into feat/ai-assistant
2 parents 40d7357 + aa4fd3f commit 732e67d

File tree

177 files changed

+8067
-6662
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

177 files changed

+8067
-6662
lines changed

.cursor/rules/api-routes.mdc

+31-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ alwaysApply: false
66
# API Routes
77

88
## Standard Format
9+
910
Use this format for API routes:
1011

1112
```ts
@@ -15,9 +16,9 @@ import { auth } from "@/app/api/auth/[...nextauth]/auth";
1516
import prisma from "@/utils/prisma";
1617
import { withError } from "@/utils/middleware";
1718

18-
const ApiNameBody = z.object({ id: z.string(), message: z.string() });
19-
export type ApiNameBody = z.infer<typeof ApiNameBody>;
20-
export type updateApiNameResponse = Awaited<ReturnType<typeof updateApiName>>;
19+
const apiNameBody = z.object({ id: z.string(), message: z.string() });
20+
export type ApiNameBody = z.infer<typeof apiNameBody>;
21+
export type UpdateApiNameResponse = Awaited<ReturnType<typeof updateApiName>>;
2122

2223
async function updateApiName(body: ApiNameBody, options: { email: string }) {
2324
const { email } = options;
@@ -26,28 +27,49 @@ async function updateApiName(body: ApiNameBody, options: { email: string }) {
2627
id: body.id,
2728
email,
2829
},
29-
data: body
30-
})
30+
data: body,
31+
});
3132

3233
return { result };
33-
};
34+
}
3435

36+
// For routes without params
3537
export const POST = withError(async (request: Request) => {
3638
const session = await auth();
37-
if (!session?.user.email) return NextResponse.json({ error: "Not authenticated" });
39+
if (!session?.user.email)
40+
return NextResponse.json({ error: "Not authenticated" });
3841

3942
const json = await request.json();
40-
const body = ApiNameBody.parse(json);
43+
const body = apiNameBody.parse(json);
4144

4245
const result = await updateApiName(body, { email: session.user.email });
4346

4447
return NextResponse.json(result);
4548
});
49+
50+
// For routes with params (note the params promise which is how Next.js 15+ works)
51+
export const GET = withError(
52+
async (
53+
request: Request,
54+
{ params }: { params: Promise<{ slug: string }> }
55+
) => {
56+
const session = await auth();
57+
if (!session?.user.email)
58+
return NextResponse.json({ error: "Not authenticated" });
59+
60+
const { slug } = await params;
61+
// Use the slug parameter...
62+
63+
return NextResponse.json({ result });
64+
}
65+
);
4666
```
4767

4868
## Implementation Guidelines
69+
4970
- Use Zod for request body validation
5071
- Create separate functions for business logic
5172
- Wrap route handlers with `withError` middleware
5273
- Always validate authentication with `auth()`
53-
- Export typed responses for client usage
74+
- Export typed responses for client usage
75+
- For routes with dynamic parameters, use the new Next.js 15+ params format with async params

.cursor/rules/cursor-rules.mdc

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ How to add new cursor rules to the project
3737

3838
5. Cursor rules have the following structure:
3939

40-
```
40+
````
4141
---
4242
description: Short description of the rule's purpose
4343
globs: optional/path/pattern/**/*
@@ -62,4 +62,5 @@ function goodExample() {
6262
function badExample() {
6363
// Implementation not following guidelines
6464
}
65-
```
65+
```
66+
````

.cursor/rules/data-fetching.mdc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
description:
2+
description: Fetching data from the API using SWR
33
globs:
44
alwaysApply: false
55
---

.cursor/rules/features/cleaner.mdc

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: false
5+
---
6+
## Inbox Cleaner
7+
8+
This file explains the Inbox Cleaner feature and how it's implemented.
9+
10+
The inbox cleaner helps users do a deep clean of their inbox.
11+
It helps them get from 10,000 items in their inbox to only a few.
12+
It works by archiving/marking read low priority emails.
13+
It uses a combination of static and AI rules to do the clean up.
14+
It uses both Postgres (Prisma) and Redis.
15+
We store short term memory in Redis that expires after a few hours. This is data like email subject so we can quickly show it to the user, but this isn't data we want stored long term to enhance privacy for the user while balancing this with a faster experience.
16+
Once the cleaning process has started we show the emails streamed in with the action taken on the email (archive/keep).
17+
18+
The main files and directories for this are:
19+
20+
- apps/web/utils/actions/clean.ts
21+
- apps/web/app/api/clean/
22+
- apps/web/app/(app)/clean/page.tsx
23+
- apps/web/app/(app)/clean/
24+
- apps/web/prisma/schema.prisma
25+
- apps/web/utils/redis/clean.ts
26+
27+
The database models to look at are:
28+
29+
- CleanupThread
30+
- CleanupJob

.cursor/rules/features/knowledge.mdc

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: false
5+
---
6+
# Knowledge Base
7+
8+
This file explains the Knowledge Base feature and how it's implemented.
9+
10+
The knowledge base helps users store and manage information that can be used to help draft responses to emails. It acts as a personal database of information that can be referenced when composing replies.
11+
12+
## Overview
13+
14+
Users can create, edit, and delete knowledge base entries. Each entry consists of:
15+
16+
- A title for quick reference
17+
- Content that contains the actual information
18+
- Metadata like creation and update timestamps
19+
20+
## Database Schema
21+
22+
The `Knowledge` model in Prisma:
23+
24+
```prisma
25+
model Knowledge {
26+
id String @id @default(cuid())
27+
createdAt DateTime @default(now())
28+
updatedAt DateTime @updatedAt
29+
title String
30+
content String
31+
32+
userId String
33+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
34+
}
35+
```
36+
37+
Each knowledge entry belongs to a specific user and is automatically deleted if the user is deleted (cascade).
38+
39+
## Main Files and Directories
40+
41+
The knowledge base functionality is implemented in:
42+
43+
- `apps/web/app/(app)/automation/knowledge/KnowledgeBase.tsx` - Main UI component
44+
- `apps/web/app/(app)/automation/knowledge/KnowledgeForm.tsx` - Form for creating/editing entries
45+
- `apps/web/utils/actions/knowledge.ts` - Server actions for CRUD operations
46+
- `apps/web/utils/actions/knowledge.validation.ts` - Zod validation schemas
47+
- `apps/web/app/api/knowledge/route.ts` - API route for fetching entries
48+
49+
### AI Integration Files
50+
51+
- `apps/web/utils/ai/knowledge/extract.ts` - Extract relevant knowledge from knowledge base entries
52+
- `apps/web/utils/ai/knowledge/extract-from-email-history.ts` - Extract context from previous emails
53+
- `apps/web/utils/ai/reply/draft-with-knowledge.ts` - Generate email drafts using extracted knowledge
54+
- `apps/web/utils/reply-tracker/generate-reply.ts` - Coordinates the extraction and drafting process
55+
- `apps/web/utils/llms/model-selector.ts` - Economy LLM selection for high-volume tasks
56+
57+
## Features
58+
59+
- **Create**: Users can add new knowledge entries with a title and content
60+
- **Read**: Entries are displayed in a table with title and last updated date
61+
- **Update**: Users can edit existing entries
62+
- **Delete**: Entries can be deleted with a confirmation dialog
63+
64+
## Usage in Email Responses
65+
66+
The knowledge base entries are used to help draft responses to emails. When composing a reply, the system can reference these entries to include relevant information, ensuring consistent and accurate responses.
67+
68+
When drafting responses, we use two LLMs:
69+
70+
1. A cheaper LLM that can process a lot of data (e.g. Google Gemini 2 Flash)
71+
2. A more expensive LLM to draft the response (e.g. Anthropic Sonnet 3.7)
72+
73+
The cheaper LLM is an agent that extracts the key information needed for the drafter LLM.
74+
For example, the knowledge base may include 100 pages of content, and the LLM extracts half a page of knowledge to pass to the more expensive drafter LLM.
75+
76+
## Dual LLM Architecture
77+
78+
The dual LLM approach is implemented as follows:
79+
80+
1. **Knowledge Extraction (Economy LLM)**:
81+
82+
- Uses a more cost-efficient model like Gemini Flash for processing large volumes of knowledge base content
83+
- Analyzes all knowledge entries and extracts only relevant information based on the email content
84+
- Configured via environment variables (`ECONOMY_LLM_PROVIDER` and `ECONOMY_LLM_MODEL`)
85+
- If no specific economy model is configured, defaults to Gemini Flash when Google API key is available
86+
87+
2. **Email Draft Generation (Core LLM)**:
88+
- Uses the default model (e.g., Anthropic Claude 3.7 Sonnet) for high-quality content generation
89+
- Receives the extracted relevant knowledge from the economy LLM
90+
- Generates the final email draft based on the provided context
91+
92+
This architecture optimizes for both cost efficiency (using cheaper models for high-volume tasks) and quality (using premium models for user-facing content).
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: false
5+
---
6+
# Reply Tracker
7+
8+
Reply Tracker (also known as Reply Zero) lets the user which emails need a reply for them and those they're awaiting a reply on.
9+
It updates the labels for the thread automatically in Gmail.
10+
11+
The database models and fields that are used for this feature:
12+
13+
- ThreadTracker
14+
- User.outboundReplyTracking
15+
- ActionType.TRACK_THREAD
16+
17+
The system uses rules. The AI can choose which rule to use each time an email comes in. Rules are for incoming emails only. Not ongoing.
18+
When enabling the reply tracker, we create a rule for the user that has the following actions associated with it:
19+
- LABEL: "To Reply"
20+
- TRACK_THREAD
21+
- DRAFT_EMAIL (optional)
22+
23+
We'll draft a reply for the user automatically if DRAFT_EMAIL is set. The draft will be generated using the email history for this sender as well as the Knowledge base. See `.cursor/rules/features/knowledge.mdc` for more on the Knowledge Base feature.
24+
25+
Enabling `User.outboundReplyTracking` means that when a user sends an email, we'll run an LLM over the email and check if it needs a reply. If it does we'll mark it as Awaiting Reply.

.cursor/rules/form-handling.mdc

+26-7
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,33 @@ alwaysApply: false
99
- The same validation should be done in the server action too
1010

1111
## Form Example
12+
1213
```tsx
14+
import { zodResolver } from "@hookform/resolvers/zod";
1315
import { Input } from "@/components/Input";
1416
import { Button } from "@/components/ui/button";
17+
import { toastSuccess, toastError } from "@/components/Toast";
18+
import { createExampleAction } from "@/utils/actions/example";
19+
import { type CreateExampleBody } from "@/utils/actions/example.validation";
1520

16-
export const ProcessHistory = () => {
21+
export const ExampleForm = () => {
1722
const {
1823
register,
1924
handleSubmit,
2025
formState: { errors, isSubmitting },
21-
} = useForm<ProcessHistoryOptions>({
26+
} = useForm<CreateExampleBody>({
2227
resolver: zodResolver(processHistorySchema),
2328
});
2429

25-
const onSubmit: SubmitHandler<ProcessHistoryOptions> = useCallback(
30+
const onSubmit: SubmitHandler<CreateExampleBody> = useCallback(
2631
async (data) => {
27-
const result = await processHistoryAction(data.email);
28-
handleActionResult(result, `Processed history for ${data.email}`);
32+
const result = await createExampleAction(data.email);
33+
34+
if (isActionError(result)) {
35+
toastError({ title: "Error", description: result.error });
36+
} else {
37+
toastSuccess({ description: "Created example!" });
38+
}
2939
},
3040
[]
3141
);
@@ -40,8 +50,17 @@ export const ProcessHistory = () => {
4050
error={errors.email}
4151
/>
4252
<Button type="submit" loading={isSubmitting}>
43-
Process History
53+
Save
4454
</Button>
4555
</form>
4656
);
47-
};
57+
};
58+
```
59+
60+
## Validation Guidelines
61+
62+
- Define validation schemas using Zod
63+
- Apply the same validation in both client and server
64+
- Use descriptive error messages
65+
- Validate form inputs before submission
66+
- Show validation errors inline next to form fields

.cursor/rules/forms-validation.mdc

-77
This file was deleted.

0 commit comments

Comments
 (0)