diff --git a/apps/unsubscriber/package.json b/apps/unsubscriber/package.json
index 7b58d8fb4b..aeceeec7ed 100644
--- a/apps/unsubscriber/package.json
+++ b/apps/unsubscriber/package.json
@@ -18,13 +18,13 @@
"typescript": "5.7.2"
},
"dependencies": {
- "@ai-sdk/amazon-bedrock": "^1.0.6",
- "@ai-sdk/anthropic": "^1.0.8",
- "@ai-sdk/google": "^1.0.12",
- "@ai-sdk/openai": "^1.0.11",
+ "@ai-sdk/amazon-bedrock": "1.1.6",
+ "@ai-sdk/anthropic": "1.1.6",
+ "@ai-sdk/google": "1.1.11",
+ "@ai-sdk/openai": "1.1.9",
"@fastify/cors": "^10.0.1",
"@t3-oss/env-core": "^0.11.1",
- "ai": "^4.0.31",
+ "ai": "4.1.32",
"dotenv": "^16.4.7",
"fastify": "^5.2.0",
"zod": "^3.24.1"
diff --git a/apps/web/app/(app)/cold-email-blocker/TestRules.tsx b/apps/web/app/(app)/cold-email-blocker/TestRules.tsx
index e587b18a57..fd24f1bfcc 100644
--- a/apps/web/app/(app)/cold-email-blocker/TestRules.tsx
+++ b/apps/web/app/(app)/cold-email-blocker/TestRules.tsx
@@ -168,6 +168,7 @@ function TestRulesContentRow(props: {
snippet: message.snippet || null,
threadId: message.threadId,
messageId: message.id,
+ date: message.internalDate || undefined,
});
}}
>
diff --git a/apps/web/app/(app)/reply-tracker/ReplyTrackerEmails.tsx b/apps/web/app/(app)/reply-tracker/ReplyTrackerEmails.tsx
index c6d5abceed..ffcf032d88 100644
--- a/apps/web/app/(app)/reply-tracker/ReplyTrackerEmails.tsx
+++ b/apps/web/app/(app)/reply-tracker/ReplyTrackerEmails.tsx
@@ -169,46 +169,51 @@ export function ReplyTrackerEmails({
}
return (
-
-
- {listView}
-
-
-
-
- {trackers.find((t) => t.threadId === selectedEmail.threadId)
- ?.resolved ? (
-
- ) : (
-
- )}
-
-
- }
- />
-
-
+ // hacky. this will break if other parts of the layout change
+
+
+
+ {listView}
+
+
+
+
+
+ {trackers.find((t) => t.threadId === selectedEmail.threadId)
+ ?.resolved ? (
+
+ ) : (
+
+ )}
+
+
+ }
+ />
+
+
+
+
);
}
@@ -250,7 +255,7 @@ function Row({
)}
onMouseEnter={onSelect}
>
-
+
e.stopPropagation()}
diff --git a/apps/web/app/(app)/reply-tracker/page.tsx b/apps/web/app/(app)/reply-tracker/page.tsx
index 15e8c19350..c3913ad1fd 100644
--- a/apps/web/app/(app)/reply-tracker/page.tsx
+++ b/apps/web/app/(app)/reply-tracker/page.tsx
@@ -68,7 +68,7 @@ export default async function ReplyTrackerPage({
- Resolved
+ Done
diff --git a/apps/web/app/(app)/simple/SimpleList.tsx b/apps/web/app/(app)/simple/SimpleList.tsx
index 513f57e376..6532406ec5 100644
--- a/apps/web/app/(app)/simple/SimpleList.tsx
+++ b/apps/web/app/(app)/simple/SimpleList.tsx
@@ -281,10 +281,6 @@ function SimpleListRow({
)}
-
- {/*
- {new Date(message.headers.date).toLocaleString()}
-
*/}
{!expanded && {actionButtons}
}
diff --git a/apps/web/app/api/ai/categorize/validation.ts b/apps/web/app/api/ai/categorize/validation.ts
index 7f7b6ccdef..1d8762c074 100644
--- a/apps/web/app/api/ai/categorize/validation.ts
+++ b/apps/web/app/api/ai/categorize/validation.ts
@@ -17,6 +17,6 @@ export const categorizeBodyWithHtml = categorizeBody.extend({
textPlain: z.string().nullable(),
textHtml: z.string().nullable(),
snippet: z.string().nullable(),
- date: z.string(),
+ internalDate: z.string(),
});
export type CategorizeBodyWithHtml = z.infer;
diff --git a/apps/web/app/api/ai/summarise/controller.ts b/apps/web/app/api/ai/summarise/controller.ts
index b6080a622f..0d01d9be5b 100644
--- a/apps/web/app/api/ai/summarise/controller.ts
+++ b/apps/web/app/api/ai/summarise/controller.ts
@@ -1,5 +1,4 @@
import { chatCompletionStream } from "@/utils/llms";
-import { Provider } from "@/utils/llms/config";
import type { UserAIFields } from "@/utils/llms/types";
import { expire } from "@/utils/redis";
import { saveSummary } from "@/utils/redis/summary";
diff --git a/apps/web/app/api/google/webhook/process-history-item.ts b/apps/web/app/api/google/webhook/process-history-item.ts
index 356faab303..6be59434aa 100644
--- a/apps/web/app/api/google/webhook/process-history-item.ts
+++ b/apps/web/app/api/google/webhook/process-history-item.ts
@@ -15,6 +15,7 @@ import type { ProcessHistoryOptions } from "@/app/api/google/webhook/types";
import { ColdEmailSetting } from "@prisma/client";
import { logger } from "@/app/api/google/webhook/logger";
import { isIgnoredSender } from "@/utils/filter-ignored-senders";
+import { internalDateToDate } from "@/utils/date";
export async function processHistoryItem(
{
@@ -138,7 +139,7 @@ export async function processHistoryItem(
content,
messageId,
threadId,
- date: message.headers.date,
+ date: internalDateToDate(message.internalDate),
},
gmail,
user,
diff --git a/apps/web/app/api/user/stats/tinybird/load/load-emails.ts b/apps/web/app/api/user/stats/tinybird/load/load-emails.ts
index 658cf6b51a..02a1ced232 100644
--- a/apps/web/app/api/user/stats/tinybird/load/load-emails.ts
+++ b/apps/web/app/api/user/stats/tinybird/load/load-emails.ts
@@ -10,6 +10,7 @@ import { findUnsubscribeLink } from "@/utils/parse/parseHtml.server";
import { env } from "@/env";
import { GmailLabel } from "@/utils/gmail/label";
import { createScopedLogger } from "@/utils/logger";
+import { internalDateToDate } from "@/utils/date";
const PAGE_SIZE = 20; // avoid setting too high because it will hit the rate limit
const PAUSE_AFTER_RATE_LIMIT = 10_000;
@@ -169,7 +170,7 @@ async function saveBatch(
? extractDomainFromEmail(m.headers.to)
: "Missing",
subject: m.headers.subject,
- timestamp: +new Date(m.headers.date),
+ timestamp: +internalDateToDate(m.internalDate),
unsubscribeLink,
read: !m.labelIds?.includes(GmailLabel.UNREAD),
sent: !!m.labelIds?.includes(GmailLabel.SENT),
@@ -182,7 +183,7 @@ async function saveBatch(
logger.error("No timestamp for email", {
ownerEmail: tinybirdEmail.ownerEmail,
gmailMessageId: tinybirdEmail.gmailMessageId,
- date: m.headers.date,
+ date: m.internalDate,
});
return;
}
diff --git a/apps/web/components/email-list/EmailAttachments.tsx b/apps/web/components/email-list/EmailAttachments.tsx
new file mode 100644
index 0000000000..46f27abaca
--- /dev/null
+++ b/apps/web/components/email-list/EmailAttachments.tsx
@@ -0,0 +1,69 @@
+import { Card } from "@/components/Card";
+import { Button } from "@/components/ui/button";
+import Link from "next/link";
+import { DownloadIcon } from "lucide-react";
+import type { ThreadMessage } from "@/components/email-list/types";
+
+export function EmailAttachments({ message }: { message: ThreadMessage }) {
+ return (
+
+ {message.attachments?.map((attachment) => {
+ const searchParams = new URLSearchParams({
+ messageId: message.id,
+ attachmentId: attachment.attachmentId,
+ mimeType: attachment.mimeType,
+ filename: attachment.filename,
+ });
+
+ const url = `/api/google/messages/attachment?${searchParams.toString()}`;
+
+ return (
+
+ {attachment.filename}
+
+
+ {mimeTypeToString(attachment.mimeType)}
+
+
+
+
+ );
+ })}
+
+ );
+}
+
+function mimeTypeToString(mimeType: string): string {
+ switch (mimeType) {
+ case "application/pdf":
+ return "PDF";
+ case "application/zip":
+ return "ZIP";
+ case "image/png":
+ return "PNG";
+ case "image/jpeg":
+ return "JPEG";
+ // LLM generated. Need to check they're actually needed
+ case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
+ return "DOCX";
+ case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
+ return "XLSX";
+ case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
+ return "PPTX";
+ case "application/vnd.ms-excel":
+ return "XLS";
+ case "application/vnd.ms-powerpoint":
+ return "PPT";
+ case "application/msword":
+ return "DOC";
+ default:
+ return mimeType;
+ }
+}
diff --git a/apps/web/components/email-list/EmailContents.tsx b/apps/web/components/email-list/EmailContents.tsx
new file mode 100644
index 0000000000..0c284f690c
--- /dev/null
+++ b/apps/web/components/email-list/EmailContents.tsx
@@ -0,0 +1,67 @@
+import { type SyntheticEvent, useCallback, useMemo, useState } from "react";
+import { Loading } from "@/components/Loading";
+
+export function HtmlEmail({ html }: { html: string }) {
+ const srcDoc = useMemo(() => getIframeHtml(html), [html]);
+ const [isLoading, setIsLoading] = useState(true);
+
+ const onLoad = useCallback(
+ (event: SyntheticEvent) => {
+ if (event.currentTarget.contentWindow) {
+ // sometimes we see minimal scrollbar, so add a buffer
+ const BUFFER = 5;
+
+ const height = `${
+ event.currentTarget.contentWindow.document.documentElement
+ .scrollHeight + BUFFER
+ }px`;
+
+ event.currentTarget.style.height = height;
+ setIsLoading(false);
+ }
+ },
+ [],
+ );
+
+ return (
+
+ {isLoading && }
+
+
+ );
+}
+
+export function PlainEmail({ text }: { text: string }) {
+ return {text};
+}
+
+function getIframeHtml(html: string) {
+ // Always inject our default font styles with lower specificity
+ // This ensures styled elements keep their fonts while unstyled ones get our defaults
+ const defaultFontStyles = `
+
+ `;
+
+ let htmlWithHead = "";
+ if (html.indexOf("") === -1) {
+ htmlWithHead = `${defaultFontStyles}${html}`;
+ } else {
+ htmlWithHead = html.replace(
+ "",
+ `${defaultFontStyles}`,
+ );
+ }
+
+ return htmlWithHead;
+}
diff --git a/apps/web/components/email-list/EmailDetails.tsx b/apps/web/components/email-list/EmailDetails.tsx
new file mode 100644
index 0000000000..accedd49d8
--- /dev/null
+++ b/apps/web/components/email-list/EmailDetails.tsx
@@ -0,0 +1,31 @@
+import type { ThreadMessage } from "@/components/email-list/types";
+
+export function EmailDetails({ message }: { message: ThreadMessage }) {
+ const details = [
+ { label: "From", value: message.headers.from },
+ { label: "To", value: message.headers.to },
+ { label: "CC", value: message.headers.cc },
+ { label: "BCC", value: message.headers.bcc },
+ {
+ label: "Date",
+ value: new Date(message.headers.date).toLocaleString(),
+ },
+ // { label: "Subject", value: message.headers.subject },
+ ];
+
+ return (
+
+
+ {details.map(
+ ({ label, value }) =>
+ value && (
+
+ {label}:
+ {value}
+
+ ),
+ )}
+
+
+ );
+}
diff --git a/apps/web/components/email-list/EmailList.tsx b/apps/web/components/email-list/EmailList.tsx
index 96f0990707..ef698c0a4d 100644
--- a/apps/web/components/email-list/EmailList.tsx
+++ b/apps/web/components/email-list/EmailList.tsx
@@ -239,7 +239,7 @@ export function EmailList({
snippet: thread.snippet,
threadId: message.threadId,
messageId: message.id,
- date: message.headers.date,
+ internalDate: message.internalDate || "",
});
if (isActionError(result)) {
diff --git a/apps/web/components/email-list/EmailMessage.tsx b/apps/web/components/email-list/EmailMessage.tsx
new file mode 100644
index 0000000000..92db6d0894
--- /dev/null
+++ b/apps/web/components/email-list/EmailMessage.tsx
@@ -0,0 +1,362 @@
+import { useCallback, useMemo, useState, useRef, useEffect } from "react";
+import {
+ ForwardIcon,
+ ReplyIcon,
+ ChevronsUpDownIcon,
+ ChevronsDownUpIcon,
+} from "lucide-react";
+import { Tooltip } from "@/components/Tooltip";
+import { extractNameFromEmail } from "@/utils/email";
+import { formatShortDate } from "@/utils/date";
+import { ComposeEmailFormLazy } from "@/app/(app)/compose/ComposeEmailFormLazy";
+import { Button } from "@/components/ui/button";
+import { Separator } from "@/components/ui/separator";
+import type { ParsedMessage } from "@/utils/types";
+import { forwardEmailHtml, forwardEmailSubject } from "@/utils/gmail/forward";
+import { extractEmailReply } from "@/utils/parse/extract-reply.client";
+import type { ReplyingToEmail } from "@/app/(app)/compose/ComposeEmailForm";
+import { createReplyContent } from "@/utils/gmail/reply";
+import { cn } from "@/utils";
+import { generateReplyAction } from "@/utils/actions/generate-reply";
+import type { ThreadMessage } from "@/components/email-list/types";
+import { EmailDetails } from "@/components/email-list/EmailDetails";
+import { HtmlEmail, PlainEmail } from "@/components/email-list/EmailContents";
+import { EmailAttachments } from "@/components/email-list/EmailAttachments";
+import { isActionError } from "@/utils/error";
+import { Loading } from "@/components/Loading";
+import { MessageText } from "@/components/Typography";
+
+export function EmailMessage({
+ message,
+ refetch,
+ showReplyButton,
+ defaultShowReply,
+ draftMessage,
+ expanded,
+ onExpand,
+ onSendSuccess,
+ generateNudge,
+}: {
+ message: ThreadMessage;
+ draftMessage?: ThreadMessage;
+ refetch: () => void;
+ showReplyButton: boolean;
+ defaultShowReply?: boolean;
+ expanded: boolean;
+ onExpand: () => void;
+ onSendSuccess: (messageId: string) => void;
+ generateNudge?: boolean;
+}) {
+ const [showReply, setShowReply] = useState(defaultShowReply || false);
+ const [showDetails, setShowDetails] = useState(false);
+
+ const onReply = useCallback(() => setShowReply(true), []);
+ const [showForward, setShowForward] = useState(false);
+ const onForward = useCallback(() => setShowForward(true), []);
+
+ const onCloseCompose = useCallback(() => {
+ setShowReply(false);
+ setShowForward(false);
+ }, []);
+
+ const toggleDetails = useCallback((e: React.MouseEvent) => {
+ e.stopPropagation();
+ setShowDetails((prev) => !prev);
+ }, []);
+
+ return (
+ // biome-ignore lint/a11y/useKeyWithClickEvents:
+
+
+
+ {expanded && (
+ <>
+ {showDetails && }
+
+ {message.textHtml ? (
+
+ ) : (
+
+ )}
+
+ {message.attachments && }
+
+ {(showReply || showForward) && (
+
+ )}
+ >
+ )}
+
+ );
+}
+
+function TopBar({
+ message,
+ expanded,
+ showDetails,
+ toggleDetails,
+ showReplyButton,
+ onReply,
+ onForward,
+}: {
+ message: ParsedMessage;
+ expanded: boolean;
+ showDetails: boolean;
+ toggleDetails: (e: React.MouseEvent) => void;
+ showReplyButton: boolean;
+ onReply: () => void;
+ onForward: () => void;
+}) {
+ return (
+
+
+
+
+
+ {extractNameFromEmail(message.headers.from)}
+ {" "}
+ wrote
+
+
+ {expanded && (
+
+ )}
+
+
+
+
+
+ {showReplyButton && (
+
+
+
+
+
+
+
+
+ )}
+
+
+ );
+}
+
+function ReplyPanel({
+ message,
+ refetch,
+ onSendSuccess,
+ onCloseCompose,
+ defaultShowReply,
+ showReply,
+ draftMessage,
+ generateNudge,
+}: {
+ message: ParsedMessage;
+ refetch: () => void;
+ onSendSuccess: (messageId: string) => void;
+ onCloseCompose: () => void;
+ defaultShowReply?: boolean;
+ showReply: boolean;
+ draftMessage?: ThreadMessage;
+ generateNudge?: boolean;
+}) {
+ const replyRef = useRef(null);
+
+ const [isGeneratingNudge, setIsGeneratingNudge] = useState(false);
+ const [nudge, setNudge] = useState(null);
+ // scroll to the reply panel when it first opens
+ useEffect(() => {
+ if (defaultShowReply && replyRef.current) {
+ // hacky using setTimeout
+ setTimeout(() => {
+ replyRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
+ }, 500);
+ }
+ }, [defaultShowReply]);
+
+ useEffect(() => {
+ async function loadNudge() {
+ setIsGeneratingNudge(true);
+
+ const isSent = message.labelIds?.includes("SENT");
+
+ const result = await generateReplyAction({
+ type: isSent ? "nudge" : "reply",
+ messages: [
+ {
+ id: message.id,
+ textHtml: message.textHtml,
+ textPlain: message.textPlain,
+ date: message.headers.date,
+ from: message.headers.from,
+ to: message.headers.to,
+ subject: message.headers.subject,
+ },
+ ],
+ });
+ if (isActionError(result)) {
+ console.error(result);
+ setNudge("");
+ } else {
+ setNudge(result.text);
+ }
+ setIsGeneratingNudge(false);
+ }
+
+ if (generateNudge) loadNudge();
+ }, [generateNudge, message]);
+
+ const replyingToEmail: ReplyingToEmail = useMemo(() => {
+ if (showReply) {
+ if (draftMessage) return prepareDraftReplyEmail(draftMessage);
+
+ // use nudge if available
+ if (nudge) {
+ // Convert nudge text into HTML paragraphs
+ const nudgeHtml = nudge
+ ? nudge
+ .split("\n")
+ .filter((line) => line.trim())
+ .map((line) => `${line}
`)
+ .join("")
+ : "";
+
+ return prepareReplyingToEmail(message, nudgeHtml);
+ }
+
+ return prepareReplyingToEmail(message);
+ }
+ return prepareForwardingEmail(message);
+ }, [showReply, message, draftMessage, nudge]);
+
+ return (
+ <>
+
+
+
+ {isGeneratingNudge ? (
+
+
+ Generating reply...
+
+
+ ) : (
+
{
+ onSendSuccess(messageId);
+ onCloseCompose();
+ }}
+ onDiscard={onCloseCompose}
+ />
+ )}
+
+ >
+ );
+}
+
+const prepareReplyingToEmail = (
+ message: ParsedMessage,
+ content = "",
+): ReplyingToEmail => {
+ const sentFromUser = message.labelIds?.includes("SENT");
+
+ const { html } = createReplyContent({ message });
+
+ return {
+ // If following an email from yourself, use original recipients, otherwise reply to sender
+ to: sentFromUser ? message.headers.to : message.headers.from,
+ // If following an email from yourself, don't add "Re:" prefix
+ subject: sentFromUser
+ ? message.headers.subject
+ : `Re: ${message.headers.subject}`,
+ headerMessageId: message.headers["message-id"]!,
+ threadId: message.threadId!,
+ // Keep original CC
+ cc: message.headers.cc,
+ // Keep original BCC if available
+ bcc: sentFromUser ? message.headers.bcc : "",
+ references: message.headers.references,
+ draftHtml: content || "",
+ quotedContentHtml: html,
+ };
+};
+
+const prepareForwardingEmail = (message: ParsedMessage): ReplyingToEmail => ({
+ to: "",
+ subject: forwardEmailSubject(message.headers.subject),
+ headerMessageId: "",
+ threadId: message.threadId!,
+ cc: "",
+ references: "",
+ draftHtml: forwardEmailHtml({ content: "", message }),
+ quotedContentHtml: "",
+});
+
+function prepareDraftReplyEmail(draft: ParsedMessage): ReplyingToEmail {
+ const splitHtml = extractEmailReply(draft.textHtml || "");
+
+ return {
+ to: draft.headers.to,
+ subject: draft.headers.subject,
+ headerMessageId: draft.headers["message-id"]!,
+ threadId: draft.threadId!,
+ cc: draft.headers.cc,
+ bcc: draft.headers.bcc,
+ references: draft.headers.references,
+ draftHtml: splitHtml.draftHtml,
+ quotedContentHtml: splitHtml.originalHtml,
+ };
+}
diff --git a/apps/web/components/email-list/EmailThread.tsx b/apps/web/components/email-list/EmailThread.tsx
index 4fd33f91da..977b93d703 100644
--- a/apps/web/components/email-list/EmailThread.tsx
+++ b/apps/web/components/email-list/EmailThread.tsx
@@ -1,36 +1,6 @@
-import {
- type SyntheticEvent,
- useCallback,
- useMemo,
- useState,
- useRef,
- useEffect,
-} from "react";
-import Link from "next/link";
-import {
- DownloadIcon,
- ForwardIcon,
- ReplyIcon,
- ChevronsUpDownIcon,
- ChevronsDownUpIcon,
-} from "lucide-react";
-import { Tooltip } from "@/components/Tooltip";
-import type { Thread } from "@/components/email-list/types";
-import { extractNameFromEmail } from "@/utils/email";
-import { formatShortDate } from "@/utils/date";
-import { ComposeEmailFormLazy } from "@/app/(app)/compose/ComposeEmailFormLazy";
-import { Button } from "@/components/ui/button";
-import { Separator } from "@/components/ui/separator";
-import { Card } from "@/components/Card";
-import type { ParsedMessage } from "@/utils/types";
-import { forwardEmailHtml, forwardEmailSubject } from "@/utils/gmail/forward";
-import { Loading } from "@/components/Loading";
-import { extractEmailReply } from "@/utils/parse/extract-reply.client";
-import type { ReplyingToEmail } from "@/app/(app)/compose/ComposeEmailForm";
-import { createReplyContent } from "@/utils/gmail/reply";
-import { cn } from "@/utils";
-
-type EmailMessage = Thread["messages"][number];
+import { useMemo, useState } from "react";
+import type { ThreadMessage } from "@/components/email-list/types";
+import { EmailMessage } from "@/components/email-list/EmailMessage";
export function EmailThread({
messages,
@@ -39,7 +9,7 @@ export function EmailThread({
autoOpenReplyForMessageId,
topRightComponent,
}: {
- messages: EmailMessage[];
+ messages: ThreadMessage[];
refetch: () => void;
showReplyButton: boolean;
autoOpenReplyForMessageId?: string;
@@ -47,8 +17,8 @@ export function EmailThread({
}) {
// Place draft messages as replies to their parent message
const organizedMessages = useMemo(() => {
- const drafts = new Map();
- const regularMessages: EmailMessage[] = [];
+ const drafts = new Map();
+ const regularMessages: ThreadMessage[] = [];
messages?.forEach((message) => {
if (message.labelIds?.includes("DRAFT")) {
@@ -87,380 +57,35 @@ export function EmailThread({
)}
- {organizedMessages.map(({ message, draftReply }) => (
- {
- setExpandedMessageIds((prev) => {
- if (prev.has(message.id)) return prev;
- return new Set(prev).add(message.id);
- });
- }}
- onSendSuccess={(messageId) => {
- setExpandedMessageIds((prev) => {
- if (prev.has(messageId)) return prev;
- return new Set(prev).add(messageId);
- });
- }}
- />
- ))}
+ {organizedMessages.map(({ message, draftReply }) => {
+ const defaultShowReply =
+ autoOpenReplyForMessageId === message.id || Boolean(draftReply);
+ return (
+ {
+ setExpandedMessageIds((prev) => {
+ if (prev.has(message.id)) return prev;
+ return new Set(prev).add(message.id);
+ });
+ }}
+ onSendSuccess={(messageId) => {
+ setExpandedMessageIds((prev) => {
+ if (prev.has(messageId)) return prev;
+ return new Set(prev).add(messageId);
+ });
+ }}
+ generateNudge={defaultShowReply && !draftReply?.textHtml}
+ />
+ );
+ })}
);
}
-
-function EmailMessage({
- message,
- refetch,
- showReplyButton,
- defaultShowReply,
- draftReply,
- expanded,
- onExpand,
- onSendSuccess,
-}: {
- message: EmailMessage;
- draftReply?: EmailMessage;
- refetch: () => void;
- showReplyButton: boolean;
- defaultShowReply?: boolean;
- expanded: boolean;
- onExpand: () => void;
- onSendSuccess: (messageId: string) => void;
-}) {
- const [showReply, setShowReply] = useState(defaultShowReply || false);
- const replyRef = useRef(null);
- const [showDetails, setShowDetails] = useState(false);
-
- useEffect(() => {
- if (defaultShowReply && replyRef.current) {
- setTimeout(() => {
- replyRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
- // NOTE: a little hacky
- // If this is set lower it doesn't work (or if we turn off autofocus, it does, but we want autofocus).
- }, 500);
- }
- }, [defaultShowReply]);
-
- const onReply = useCallback(() => setShowReply(true), []);
- const [showForward, setShowForward] = useState(false);
- const onForward = useCallback(() => setShowForward(true), []);
-
- const onCloseCompose = useCallback(() => {
- setShowReply(false);
- setShowForward(false);
- }, []);
-
- const replyingToEmail: ReplyingToEmail = useMemo(() => {
- if (showReply) {
- if (draftReply) return prepareDraftReplyEmail(draftReply);
- return prepareReplyingToEmail(message);
- }
- return prepareForwardingEmail(message);
- }, [showReply, message, draftReply]);
-
- const toggleDetails = useCallback((e: React.MouseEvent) => {
- e.stopPropagation();
- setShowDetails((prev) => !prev);
- }, []);
-
- return (
- // biome-ignore lint/a11y/useKeyWithClickEvents:
-
-
-
-
-
-
- {extractNameFromEmail(message.headers.from)}
- {" "}
- wrote
-
-
- {expanded && (
-
- )}
-
-
-
-
-
- {showReplyButton && (
-
-
-
-
-
-
-
-
- )}
-
-
-
- {expanded && (
- <>
- {showDetails && }
-
- {message.textHtml ? (
-
- ) : (
-
- )}
-
- {message.attachments && (
-
- {message.attachments.map((attachment) => {
- const url = `/api/google/messages/attachment?messageId=${message.id}&attachmentId=${attachment.attachmentId}&mimeType=${attachment.mimeType}&filename=${attachment.filename}`;
-
- return (
-
- {attachment.filename}
-
-
- {mimeTypeToString(attachment.mimeType)}
-
-
-
-
- );
- })}
-
- )}
-
- {(showReply || showForward) && (
- <>
-
-
-
- {
- onSendSuccess(messageId);
- onCloseCompose();
- }}
- onDiscard={onCloseCompose}
- />
-
- >
- )}
- >
- )}
-
- );
-}
-
-export function HtmlEmail({ html }: { html: string }) {
- const srcDoc = useMemo(() => getIframeHtml(html), [html]);
- const [isLoading, setIsLoading] = useState(true);
-
- const onLoad = useCallback(
- (event: SyntheticEvent) => {
- if (event.currentTarget.contentWindow) {
- // sometimes we see minimal scrollbar, so add a buffer
- const BUFFER = 5;
-
- const height = `${
- event.currentTarget.contentWindow.document.documentElement
- .scrollHeight + BUFFER
- }px`;
-
- event.currentTarget.style.height = height;
- setIsLoading(false);
- }
- },
- [],
- );
-
- return (
-
- {isLoading && }
-
-
- );
-}
-
-function PlainEmail({ text }: { text: string }) {
- return {text};
-}
-
-function getIframeHtml(html: string) {
- // Always inject our default font styles with lower specificity
- // This ensures styled elements keep their fonts while unstyled ones get our defaults
- const defaultFontStyles = `
-
- `;
-
- let htmlWithHead = "";
- if (html.indexOf("") === -1) {
- htmlWithHead = `${defaultFontStyles}${html}`;
- } else {
- htmlWithHead = html.replace(
- "",
- `${defaultFontStyles}`,
- );
- }
-
- return htmlWithHead;
-}
-
-function mimeTypeToString(mimeType: string): string {
- switch (mimeType) {
- case "application/pdf":
- return "PDF";
- case "application/zip":
- return "ZIP";
- case "image/png":
- return "PNG";
- case "image/jpeg":
- return "JPEG";
- // LLM generated. Need to check they're actually needed
- case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
- return "DOCX";
- case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
- return "XLSX";
- case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
- return "PPTX";
- case "application/vnd.ms-excel":
- return "XLS";
- case "application/vnd.ms-powerpoint":
- return "PPT";
- case "application/vnd.ms-word":
- return "DOC";
- default:
- return mimeType;
- }
-}
-
-const prepareReplyingToEmail = (message: ParsedMessage): ReplyingToEmail => {
- const sentFromUser = message.labelIds?.includes("SENT");
-
- const { html } = createReplyContent({ message });
-
- return {
- // If following an email from yourself, use original recipients, otherwise reply to sender
- to: sentFromUser ? message.headers.to : message.headers.from,
- // If following an email from yourself, don't add "Re:" prefix
- subject: sentFromUser
- ? message.headers.subject
- : `Re: ${message.headers.subject}`,
- headerMessageId: message.headers["message-id"]!,
- threadId: message.threadId!,
- // Keep original CC
- cc: message.headers.cc,
- // Keep original BCC if available
- bcc: sentFromUser ? message.headers.bcc : "",
- references: message.headers.references,
- draftHtml: "",
- quotedContentHtml: html,
- };
-};
-
-const prepareForwardingEmail = (message: ParsedMessage): ReplyingToEmail => ({
- to: "",
- subject: forwardEmailSubject(message.headers.subject),
- headerMessageId: "",
- threadId: message.threadId!,
- cc: "",
- references: "",
- draftHtml: forwardEmailHtml({ content: "", message }),
- quotedContentHtml: "",
-});
-
-function prepareDraftReplyEmail(message: ParsedMessage): ReplyingToEmail {
- const splitHtml = extractEmailReply(message.textHtml || "");
-
- return {
- to: message.headers.to,
- subject: message.headers.subject,
- headerMessageId: message.headers["message-id"]!,
- threadId: message.threadId!,
- cc: message.headers.cc,
- bcc: message.headers.bcc,
- references: message.headers.references,
- draftHtml: splitHtml.draftHtml,
- quotedContentHtml: splitHtml.originalHtml,
- };
-}
-
-function EmailDetails({ message }: { message: EmailMessage }) {
- const details = [
- { label: "From", value: message.headers.from },
- { label: "To", value: message.headers.to },
- { label: "CC", value: message.headers.cc },
- { label: "BCC", value: message.headers.bcc },
- {
- label: "Date",
- value: new Date(message.headers.date).toLocaleString(),
- },
- // { label: "Subject", value: message.headers.subject },
- ];
-
- return (
-
-
- {details.map(
- ({ label, value }) =>
- value && (
-
- {label}:
- {value}
-
- ),
- )}
-
-
- );
-}
diff --git a/apps/web/components/email-list/types.ts b/apps/web/components/email-list/types.ts
index b91d4484ca..cc607dbb1b 100644
--- a/apps/web/components/email-list/types.ts
+++ b/apps/web/components/email-list/types.ts
@@ -11,3 +11,5 @@ export type Thread = {
};
export type Executing = Record;
+
+export type ThreadMessage = Thread["messages"][number];
diff --git a/apps/web/hooks/useThreadsByIds.ts b/apps/web/hooks/useThreadsByIds.ts
index 98d0034222..1f8a578425 100644
--- a/apps/web/hooks/useThreadsByIds.ts
+++ b/apps/web/hooks/useThreadsByIds.ts
@@ -5,8 +5,7 @@ export function useThreadsByIds(
{ threadIds }: { threadIds: string[] },
options?: { keepPreviousData?: boolean },
) {
- const searchParams = new URLSearchParams();
- searchParams.set("threadIds", threadIds.join(","));
+ const searchParams = new URLSearchParams({ threadIds: threadIds.join(",") });
const url = `/api/google/threads/batch?${searchParams.toString()}`;
const { data, isLoading, error, mutate } = useSWR(
threadIds.length ? url : null,
diff --git a/apps/web/package.json b/apps/web/package.json
index 88fe536d0a..6e6582c289 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -13,11 +13,11 @@
"postinstall": "prisma generate"
},
"dependencies": {
- "@ai-sdk/amazon-bedrock": "^1.0.6",
- "@ai-sdk/anthropic": "^1.0.8",
- "@ai-sdk/google": "^1.0.12",
- "@ai-sdk/groq": "^1.0.10",
- "@ai-sdk/openai": "^1.0.11",
+ "@ai-sdk/amazon-bedrock": "1.1.6",
+ "@ai-sdk/anthropic": "1.1.6",
+ "@ai-sdk/google": "1.1.11",
+ "@ai-sdk/groq": "1.1.7",
+ "@ai-sdk/openai": "1.1.9",
"@asteasolutions/zod-to-openapi": "^7.3.0",
"@auth/core": "^0.37.4",
"@auth/prisma-adapter": "^2.7.4",
@@ -74,7 +74,7 @@
"@upstash/qstash": "^2.7.20",
"@upstash/redis": "^1.34.3",
"@vercel/analytics": "^1.4.1",
- "ai": "^4.0.31",
+ "ai": "4.1.32",
"capital-case": "^2.0.0",
"cheerio": "1.0.0",
"class-variance-authority": "^0.7.1",
diff --git a/apps/web/providers/SWRProvider.tsx b/apps/web/providers/SWRProvider.tsx
index 278874ac5e..15741f520d 100644
--- a/apps/web/providers/SWRProvider.tsx
+++ b/apps/web/providers/SWRProvider.tsx
@@ -6,6 +6,10 @@ import { captureException } from "@/utils/error";
// https://swr.vercel.app/docs/error-handling#status-code-and-error-object
const fetcher = async (url: string, init?: RequestInit | undefined) => {
+ // Super hacky, if we use streaming endpoints we should do this:
+ // https://github.com/vercel/ai/issues/3214
+ // if (url.startsWith("/api/ai/")) return [];
+
const res = await fetch(url, init);
if (!res.ok) {
diff --git a/apps/web/utils/actions/categorize-email.ts b/apps/web/utils/actions/categorize-email.ts
index 3283c6b424..a0c369bb98 100644
--- a/apps/web/utils/actions/categorize-email.ts
+++ b/apps/web/utils/actions/categorize-email.ts
@@ -13,6 +13,7 @@ import { truncate } from "@/utils/string";
import { withActionInstrumentation } from "@/utils/actions/middleware";
import { validateUserAndAiAccess } from "@/utils/user/validate";
import { isActionError } from "@/utils/error";
+import { internalDateToDate } from "@/utils/date";
export const categorizeEmailAction = withActionInstrumentation(
"categorizeEmail",
@@ -39,7 +40,11 @@ export const categorizeEmailAction = withActionInstrumentation(
});
const unsubscribeLink = findUnsubscribeLink(data.textHtml);
- const hasPreviousEmail = await hasPreviousEmailsFromSender(gmail, data);
+ const hasPreviousEmail = await hasPreviousEmailsFromSender(gmail, {
+ from: data.from,
+ messageId: data.messageId,
+ date: internalDateToDate(data.internalDate),
+ });
const res = await categorize(
{
diff --git a/apps/web/utils/actions/cold-email.ts b/apps/web/utils/actions/cold-email.ts
index 482355b802..bd946b1fc8 100644
--- a/apps/web/utils/actions/cold-email.ts
+++ b/apps/web/utils/actions/cold-email.ts
@@ -122,7 +122,7 @@ async function checkColdEmail(
from: body.from,
subject: body.subject,
content,
- date: body.date,
+ date: body.date ? new Date(body.date) : undefined,
threadId: body.threadId || undefined,
messageId: body.messageId,
},
diff --git a/apps/web/utils/actions/generate-reply.ts b/apps/web/utils/actions/generate-reply.ts
new file mode 100644
index 0000000000..1b5d059438
--- /dev/null
+++ b/apps/web/utils/actions/generate-reply.ts
@@ -0,0 +1,62 @@
+"use server";
+
+import { auth } from "@/app/api/auth/[...nextauth]/auth";
+import {
+ generateReplySchema,
+ type GenerateReplySchema,
+} from "@/utils/actions/generate-reply.validation";
+import { withActionInstrumentation } from "@/utils/actions/middleware";
+import { aiGenerateNudge } from "@/utils/ai/reply/generate-nudge";
+import { aiGenerateReply } from "@/utils/ai/reply/generate-reply";
+import { emailToContent } from "@/utils/mail";
+import { getReply, saveReply } from "@/utils/redis/reply";
+import { getAiUserByEmail } from "@/utils/user/get";
+
+export const generateReplyAction = withActionInstrumentation(
+ "generateReply",
+ async (unsafeData: GenerateReplySchema) => {
+ const session = await auth();
+ if (!session?.user.email) return { error: "Not authenticated" };
+
+ const user = await getAiUserByEmail({ email: session.user.email });
+
+ if (!user) return { error: "User not found" };
+
+ const { data, error } = generateReplySchema.safeParse(unsafeData);
+ if (error) return { error: error.message };
+
+ const lastMessage = data.messages.at(-1);
+
+ if (!lastMessage) return { error: "No message provided" };
+
+ const reply = await getReply({
+ userId: user.id,
+ messageId: lastMessage.id,
+ });
+
+ if (reply) return { text: reply };
+
+ const messages = data.messages.map((msg) => ({
+ ...msg,
+ date: new Date(msg.date),
+ content: emailToContent({
+ textPlain: msg.textPlain,
+ textHtml: msg.textHtml,
+ snippet: "",
+ }),
+ }));
+
+ const text =
+ data.type === "nudge"
+ ? await aiGenerateNudge({ messages, user })
+ : await aiGenerateReply({ messages, user });
+
+ await saveReply({
+ userId: user.id,
+ messageId: lastMessage.id,
+ reply: text,
+ });
+
+ return { text };
+ },
+);
diff --git a/apps/web/utils/actions/generate-reply.validation.ts b/apps/web/utils/actions/generate-reply.validation.ts
new file mode 100644
index 0000000000..09b52dcd44
--- /dev/null
+++ b/apps/web/utils/actions/generate-reply.validation.ts
@@ -0,0 +1,22 @@
+import { z } from "zod";
+
+const messageSchema = z
+ .object({
+ id: z.string(),
+ from: z.string(),
+ to: z.string(),
+ subject: z.string(),
+ textPlain: z.string().optional(),
+ textHtml: z.string().optional(),
+ date: z.string(),
+ })
+ .refine((data) => data.textPlain || data.textHtml, {
+ message: "At least one of textPlain or textHtml is required",
+ });
+
+export const generateReplySchema = z.object({
+ type: z.enum(["reply", "nudge"]),
+ messages: z.array(messageSchema),
+});
+
+export type GenerateReplySchema = z.infer;
diff --git a/apps/web/utils/ai/assistant/process-user-request.ts b/apps/web/utils/ai/assistant/process-user-request.ts
index f78a8c37d5..d89d049a3b 100644
--- a/apps/web/utils/ai/assistant/process-user-request.ts
+++ b/apps/web/utils/ai/assistant/process-user-request.ts
@@ -26,8 +26,8 @@ import {
} from "@/utils/rule/rule";
import { updateCategoryForSender } from "@/utils/categorize/senders/categorize";
import { findSenderByEmail } from "@/utils/sender";
-import { getEmailForLLM } from "@/utils/ai/choose-rule/get-email-from-message";
-import { stringifyEmailSimple } from "@/utils/ai/choose-rule/stringify-email";
+import { getEmailForLLM } from "@/utils/get-email-from-message";
+import { stringifyEmailSimple } from "@/utils/stringify-email";
import {
updatePromptFileOnRuleCreated,
updatePromptFileOnRuleUpdated,
diff --git a/apps/web/utils/ai/choose-rule/ai-choose-args.ts b/apps/web/utils/ai/choose-rule/ai-choose-args.ts
index 85a97acad9..2009d54e1b 100644
--- a/apps/web/utils/ai/choose-rule/ai-choose-args.ts
+++ b/apps/web/utils/ai/choose-rule/ai-choose-args.ts
@@ -2,7 +2,7 @@ import { z } from "zod";
import type { UserAIFields } from "@/utils/llms/types";
import type { Action, User } from "@prisma/client";
import { chatCompletionTools, withRetry } from "@/utils/llms";
-import { stringifyEmail } from "@/utils/ai/choose-rule/stringify-email";
+import { stringifyEmail } from "@/utils/stringify-email";
import {
type EmailForLLM,
type RuleWithActions,
diff --git a/apps/web/utils/ai/choose-rule/ai-choose-rule.ts b/apps/web/utils/ai/choose-rule/ai-choose-rule.ts
index 59ca554def..2274b41ea6 100644
--- a/apps/web/utils/ai/choose-rule/ai-choose-rule.ts
+++ b/apps/web/utils/ai/choose-rule/ai-choose-rule.ts
@@ -2,7 +2,7 @@ import { z } from "zod";
import type { UserAIFields } from "@/utils/llms/types";
import { chatCompletionObject } from "@/utils/llms";
import type { User } from "@prisma/client";
-import { stringifyEmail } from "@/utils/ai/choose-rule/stringify-email";
+import { stringifyEmail } from "@/utils/stringify-email";
import type { EmailForLLM } from "@/utils/types";
import { createScopedLogger } from "@/utils/logger";
diff --git a/apps/web/utils/ai/choose-rule/match-rules.ts b/apps/web/utils/ai/choose-rule/match-rules.ts
index a60fc699fb..0c233b1ca4 100644
--- a/apps/web/utils/ai/choose-rule/match-rules.ts
+++ b/apps/web/utils/ai/choose-rule/match-rules.ts
@@ -16,7 +16,7 @@ import {
} from "@prisma/client";
import prisma from "@/utils/prisma";
import { aiChooseRule } from "@/utils/ai/choose-rule/ai-choose-rule";
-import { getEmailForLLM } from "@/utils/ai/choose-rule/get-email-from-message";
+import { getEmailForLLM } from "@/utils/get-email-from-message";
import { isReplyInThread } from "@/utils/thread";
import type { UserAIFields } from "@/utils/llms/types";
import { createScopedLogger } from "@/utils/logger";
diff --git a/apps/web/utils/ai/choose-rule/run-rules.ts b/apps/web/utils/ai/choose-rule/run-rules.ts
index 237d67adac..665dcc1d86 100644
--- a/apps/web/utils/ai/choose-rule/run-rules.ts
+++ b/apps/web/utils/ai/choose-rule/run-rules.ts
@@ -14,7 +14,7 @@ import type { ActionItem } from "@/utils/ai/types";
import { findMatchingRule } from "@/utils/ai/choose-rule/match-rules";
import { getActionItemsWithAiArgs } from "@/utils/ai/choose-rule/ai-choose-args";
import { executeAct } from "@/utils/ai/choose-rule/execute";
-import { getEmailForLLM } from "@/utils/ai/choose-rule/get-email-from-message";
+import { getEmailForLLM } from "@/utils/get-email-from-message";
import prisma from "@/utils/prisma";
import { createScopedLogger } from "@/utils/logger";
import type { MatchReason } from "@/utils/ai/choose-rule/types";
diff --git a/apps/web/utils/ai/reply/check-if-needs-reply.ts b/apps/web/utils/ai/reply/check-if-needs-reply.ts
index 22c30208d7..4507e91e0b 100644
--- a/apps/web/utils/ai/reply/check-if-needs-reply.ts
+++ b/apps/web/utils/ai/reply/check-if-needs-reply.ts
@@ -7,7 +7,7 @@ import type { EmailForLLM } from "@/utils/types";
import {
stringifyEmailFromBody,
stringifyEmailSimple,
-} from "@/utils/ai/choose-rule/stringify-email";
+} from "@/utils/stringify-email";
const logger = createScopedLogger("check-if-needs-reply");
diff --git a/apps/web/utils/ai/reply/generate-nudge.ts b/apps/web/utils/ai/reply/generate-nudge.ts
new file mode 100644
index 0000000000..3af95d7b31
--- /dev/null
+++ b/apps/web/utils/ai/reply/generate-nudge.ts
@@ -0,0 +1,58 @@
+import { chatCompletion } from "@/utils/llms";
+import type { UserEmailWithAI } from "@/utils/llms/types";
+import { stringifyEmail } from "@/utils/stringify-email";
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("generate-nudge");
+
+export async function aiGenerateNudge({
+ messages,
+ user,
+}: {
+ messages: {
+ from: string;
+ to: string;
+ subject: string;
+ content: string;
+ date: Date;
+ }[];
+ user: UserEmailWithAI;
+ onFinish?: (completion: string) => Promise;
+}) {
+ const system = `You are an expert at writing follow-up emails that get responses.
+Write a polite and professional email that follows up on the previous conversation.
+Keep it concise and friendly. Don't be pushy.
+Use context from the previous emails to make it relevant.
+Don't mention that you're an AI.
+Don't reply with a Subject. Only reply with the body of the email.
+Keep it short.`;
+
+ const prompt = `Here is the context of the email thread (from oldest to newest):
+${messages
+ .map(
+ (msg) => `
+${stringifyEmail(msg, 3000)}
+${msg.date.toISOString()}
+`,
+ )
+ .join("\n")}
+
+Write a brief follow-up email to politely nudge for a response.
+
+Today's date is: ${new Date().toISOString().split("T")[0]}.
+IMPORTANT: The person you're writing an email for is: ${messages.at(-1)?.from}.`;
+
+ logger.trace("Input", { system, prompt });
+
+ const response = await chatCompletion({
+ userAi: user,
+ system,
+ prompt,
+ userEmail: user.email,
+ usageLabel: "Reply",
+ });
+
+ logger.trace("Output", { response: response.text });
+
+ return response.text;
+}
diff --git a/apps/web/utils/ai/reply/generate-reply.ts b/apps/web/utils/ai/reply/generate-reply.ts
new file mode 100644
index 0000000000..1f3b0d78cb
--- /dev/null
+++ b/apps/web/utils/ai/reply/generate-reply.ts
@@ -0,0 +1,56 @@
+import { chatCompletion } from "@/utils/llms";
+import type { UserEmailWithAI } from "@/utils/llms/types";
+import { stringifyEmail } from "@/utils/stringify-email";
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("generate-reply");
+
+export async function aiGenerateReply({
+ messages,
+ user,
+}: {
+ messages: {
+ from: string;
+ to: string;
+ subject: string;
+ content: string;
+ date: Date;
+ }[];
+ user: UserEmailWithAI;
+}) {
+ const system = `You are an expert assistant that drafts email replies.
+Write a polite and professional email that follows up on the previous conversation.
+Keep it concise and friendly. Don't be pushy.
+Use context from the previous emails to make it relevant.
+Don't mention that you're an AI.
+Don't reply with a Subject. Only reply with the body of the email.
+Keep it short.`;
+
+ const prompt = `Here is the context of the email thread (from oldest to newest):
+${messages
+ .map(
+ (msg) => `
+${stringifyEmail(msg, 3000)}
+${msg.date.toISOString()}
+`,
+ )
+ .join("\n")}
+
+Please write a reply to the email.
+Today's date is: ${new Date().toISOString().split("T")[0]}.
+IMPORTANT: The person you're writing an email for is: ${messages.at(-1)?.to}.`;
+
+ logger.trace("Input", { system, prompt });
+
+ const response = await chatCompletion({
+ userAi: user,
+ system,
+ prompt,
+ userEmail: user.email,
+ usageLabel: "Reply",
+ });
+
+ logger.trace("Output", { response: response.text });
+
+ return response.text;
+}
diff --git a/apps/web/utils/ai/rule/rule-fix.ts b/apps/web/utils/ai/rule/rule-fix.ts
index 55dc531f16..ceaee087d4 100644
--- a/apps/web/utils/ai/rule/rule-fix.ts
+++ b/apps/web/utils/ai/rule/rule-fix.ts
@@ -1,5 +1,5 @@
import { z } from "zod";
-import { stringifyEmail } from "@/utils/ai/choose-rule/stringify-email";
+import { stringifyEmail } from "@/utils/stringify-email";
import type { EmailForLLM } from "@/utils/types";
import { chatCompletionObject } from "@/utils/llms";
import type { UserAIFields } from "@/utils/llms/types";
diff --git a/apps/web/utils/ai/snippets/find-snippets.ts b/apps/web/utils/ai/snippets/find-snippets.ts
index 91189fb3e3..a3c30da99d 100644
--- a/apps/web/utils/ai/snippets/find-snippets.ts
+++ b/apps/web/utils/ai/snippets/find-snippets.ts
@@ -1,5 +1,5 @@
import { z } from "zod";
-import { stringifyEmail } from "@/utils/ai/choose-rule/stringify-email";
+import { stringifyEmail } from "@/utils/stringify-email";
import type { EmailForLLM } from "@/utils/types";
import { chatCompletionObject } from "@/utils/llms";
import type { UserAIFields } from "@/utils/llms/types";
diff --git a/apps/web/utils/assistant/process-assistant-email.ts b/apps/web/utils/assistant/process-assistant-email.ts
index 07188e8a93..856b92cd08 100644
--- a/apps/web/utils/assistant/process-assistant-email.ts
+++ b/apps/web/utils/assistant/process-assistant-email.ts
@@ -10,6 +10,7 @@ import { replyToEmail } from "@/utils/gmail/mail";
import { getThreadMessages } from "@/utils/gmail/thread";
import { isAssistantEmail } from "@/utils/assistant/is-assistant-email";
import { getOrCreateInboxZeroLabel, labelMessage } from "@/utils/gmail/label";
+import { internalDateToDate } from "@/utils/date";
const logger = createScopedLogger("process-assistant-email");
@@ -169,12 +170,14 @@ async function processAssistantEmailInternal({
return;
}
- const firstMessageToAssistantDate = new Date(
- firstMessageToAssistant.headers.date,
+ const firstMessageToAssistantDate = internalDateToDate(
+ firstMessageToAssistant.internalDate,
);
const messages = threadMessages
- .filter((m) => new Date(m.headers.date) >= firstMessageToAssistantDate)
+ .filter(
+ (m) => internalDateToDate(m.internalDate) >= firstMessageToAssistantDate,
+ )
.map((m) => {
const isAssistant = isAssistantEmail({
userEmail,
diff --git a/apps/web/utils/auth.ts b/apps/web/utils/auth.ts
index 4683d6390b..90807ffab0 100644
--- a/apps/web/utils/auth.ts
+++ b/apps/web/utils/auth.ts
@@ -92,24 +92,24 @@ export const getAuthOptions: (options?: {
return token;
}
- logger.info("JWT callback - current token state", {
- email: token.email,
- currentExpiresAt: token.expires_at
- ? new Date((token.expires_at as number) * 1000).toISOString()
- : "not set",
- });
+ // logger.info("JWT callback - current token state", {
+ // email: token.email,
+ // currentExpiresAt: token.expires_at
+ // ? new Date((token.expires_at as number) * 1000).toISOString()
+ // : "not set",
+ // });
if (
token.expires_at &&
Date.now() < (token.expires_at as number) * 1000
) {
- // If the access token has not expired yet, return it
- logger.info("Token still valid", {
- email: token.email,
- expiresIn:
- ((token.expires_at as number) * 1000 - Date.now()) / 1000 / 60,
- minutes: true,
- });
+ // // If the access token has not expired yet, return it
+ // logger.info("Token still valid", {
+ // email: token.email,
+ // expiresIn:
+ // ((token.expires_at as number) * 1000 - Date.now()) / 1000 / 60,
+ // minutes: true,
+ // });
return token;
}
diff --git a/apps/web/utils/cold-email/is-cold-email.ts b/apps/web/utils/cold-email/is-cold-email.ts
index 27055d3fe1..203b99c88e 100644
--- a/apps/web/utils/cold-email/is-cold-email.ts
+++ b/apps/web/utils/cold-email/is-cold-email.ts
@@ -7,7 +7,7 @@ import { labelMessage } from "@/utils/gmail/label";
import { ColdEmailSetting, ColdEmailStatus, type User } from "@prisma/client";
import prisma from "@/utils/prisma";
import { DEFAULT_COLD_EMAIL_PROMPT } from "@/utils/cold-email/prompt";
-import { stringifyEmail } from "@/utils/ai/choose-rule/stringify-email";
+import { stringifyEmail } from "@/utils/stringify-email";
import { createScopedLogger } from "@/utils/logger";
import { hasPreviousEmailsFromSenderOrDomain } from "@/utils/gmail/message";
@@ -29,7 +29,7 @@ export async function isColdEmail({
from: string;
subject: string;
content: string;
- date?: string;
+ date?: Date;
threadId?: string;
messageId: string | null;
};
@@ -161,7 +161,7 @@ export async function runColdEmailBlocker(options: {
content: string;
messageId: string;
threadId: string;
- date: string;
+ date: Date;
};
gmail: gmail_v1.Gmail;
user: Pick &
diff --git a/apps/web/utils/ai/choose-rule/get-email-from-message.ts b/apps/web/utils/get-email-from-message.ts
similarity index 82%
rename from apps/web/utils/ai/choose-rule/get-email-from-message.ts
rename to apps/web/utils/get-email-from-message.ts
index 536edb122c..f1c268338e 100644
--- a/apps/web/utils/ai/choose-rule/get-email-from-message.ts
+++ b/apps/web/utils/get-email-from-message.ts
@@ -1,5 +1,6 @@
import type { ParsedMessage, EmailForLLM } from "@/utils/types";
import { emailToContent, type EmailToContentOptions } from "@/utils/mail";
+import { internalDateToDate } from "@/utils/date";
export function getEmailForLLM(
message: ParsedMessage,
@@ -11,5 +12,6 @@ export function getEmailForLLM(
cc: message.headers.cc,
subject: message.headers.subject,
content: emailToContent(message, contentOptions),
+ date: internalDateToDate(message.internalDate),
};
}
diff --git a/apps/web/utils/gmail/message.ts b/apps/web/utils/gmail/message.ts
index 0cba2ddea1..0f503ed8dd 100644
--- a/apps/web/utils/gmail/message.ts
+++ b/apps/web/utils/gmail/message.ts
@@ -101,7 +101,7 @@ async function findPreviousEmailsBySender(
export async function hasPreviousEmailsFromSender(
gmail: gmail_v1.Gmail,
- options: { from: string; date: string; messageId: string },
+ options: { from: string; date: Date; messageId: string },
) {
const previousEmails = await findPreviousEmailsBySender(gmail, {
sender: options.from,
@@ -133,7 +133,7 @@ const PUBLIC_DOMAINS = new Set([
export async function hasPreviousEmailsFromSenderOrDomain(
gmail: gmail_v1.Gmail,
- options: { from: string; date: string; messageId: string },
+ options: { from: string; date: Date; messageId: string },
) {
const domain = extractDomainFromEmail(options.from);
if (!domain) return hasPreviousEmailsFromSender(gmail, options);
diff --git a/apps/web/utils/llms/index.ts b/apps/web/utils/llms/index.ts
index 0c2da2178b..5dedc876a3 100644
--- a/apps/web/utils/llms/index.ts
+++ b/apps/web/utils/llms/index.ts
@@ -108,6 +108,46 @@ function getModel({ aiProvider, aiModel, aiApiKey }: UserAIFields) {
throw new Error("AI provider not supported");
}
+export async function chatCompletion({
+ userAi,
+ prompt,
+ system,
+ userEmail,
+ usageLabel,
+}: {
+ userAi: UserAIFields;
+ prompt: string;
+ system?: string;
+ userEmail: string;
+ usageLabel: string;
+}) {
+ try {
+ const { provider, model, llmModel } = getModel(userAi);
+
+ const result = await generateText({
+ model: llmModel,
+ prompt,
+ system,
+ experimental_telemetry: { isEnabled: true },
+ });
+
+ if (result.usage) {
+ await saveAiUsage({
+ email: userEmail,
+ usage: result.usage,
+ provider,
+ model,
+ label: usageLabel,
+ });
+ }
+
+ return result;
+ } catch (error) {
+ await handleError(error, userEmail);
+ throw error;
+ }
+}
+
type ChatCompletionObjectArgs = {
userAi: UserAIFields;
prompt: string;
diff --git a/apps/web/utils/redis/reply.ts b/apps/web/utils/redis/reply.ts
new file mode 100644
index 0000000000..2894b0f166
--- /dev/null
+++ b/apps/web/utils/redis/reply.ts
@@ -0,0 +1,35 @@
+import { redis } from "@/utils/redis";
+
+function getReplyKey({
+ userId,
+ messageId,
+}: {
+ userId: string;
+ messageId: string;
+}) {
+ return `reply:${userId}:${messageId}`;
+}
+
+export async function getReply({
+ userId,
+ messageId,
+}: {
+ userId: string;
+ messageId: string;
+}): Promise {
+ return redis.get(getReplyKey({ userId, messageId }));
+}
+
+export async function saveReply({
+ userId,
+ messageId,
+ reply,
+}: {
+ userId: string;
+ messageId: string;
+ reply: string;
+}) {
+ return redis.set(getReplyKey({ userId, messageId }), reply, {
+ ex: 60 * 60 * 24, // 1 day
+ });
+}
diff --git a/apps/web/utils/reply-tracker/inbound.ts b/apps/web/utils/reply-tracker/inbound.ts
index d58422c71e..31f556d428 100644
--- a/apps/web/utils/reply-tracker/inbound.ts
+++ b/apps/web/utils/reply-tracker/inbound.ts
@@ -11,7 +11,7 @@ import type { UserEmailWithAI } from "@/utils/llms/types";
import type { User } from "@prisma/client";
import type { ParsedMessage } from "@/utils/types";
import { internalDateToDate } from "@/utils/date";
-import { getEmailForLLM } from "@/utils/ai/choose-rule/get-email-from-message";
+import { getEmailForLLM } from "@/utils/get-email-from-message";
import { aiChooseRule } from "@/utils/ai/choose-rule/ai-choose-rule";
import { getReplyTrackingRule } from "@/utils/reply-tracker";
diff --git a/apps/web/utils/reply-tracker/outbound.ts b/apps/web/utils/reply-tracker/outbound.ts
index 916b4fedee..cd6ef73d1d 100644
--- a/apps/web/utils/reply-tracker/outbound.ts
+++ b/apps/web/utils/reply-tracker/outbound.ts
@@ -6,7 +6,7 @@ import prisma from "@/utils/prisma";
import { getThreadMessages } from "@/utils/gmail/thread";
import { ThreadTrackerType, type User } from "@prisma/client";
import { createScopedLogger } from "@/utils/logger";
-import { getEmailForLLM } from "@/utils/ai/choose-rule/get-email-from-message";
+import { getEmailForLLM } from "@/utils/get-email-from-message";
import {
labelAwaitingReply,
removeNeedsReplyLabel,
diff --git a/apps/web/utils/ai/choose-rule/stringify-email.ts b/apps/web/utils/stringify-email.ts
similarity index 100%
rename from apps/web/utils/ai/choose-rule/stringify-email.ts
rename to apps/web/utils/stringify-email.ts
diff --git a/apps/web/utils/types.ts b/apps/web/utils/types.ts
index 34fb1074ac..d666b523e2 100644
--- a/apps/web/utils/types.ts
+++ b/apps/web/utils/types.ts
@@ -93,7 +93,7 @@ export interface ParsedMessageHeaders {
to: string;
cc?: string;
bcc?: string;
- date: string;
+ date: string; // the date supplied by the email. internally we rely on message.internalDate provided by the gmail api
"message-id"?: string;
"reply-to"?: string;
"in-reply-to"?: string;
@@ -107,4 +107,5 @@ export type EmailForLLM = {
cc?: string;
subject: string;
content: string;
+ date?: Date;
};
diff --git a/apps/web/utils/user/get.ts b/apps/web/utils/user/get.ts
new file mode 100644
index 0000000000..477c80579d
--- /dev/null
+++ b/apps/web/utils/user/get.ts
@@ -0,0 +1,15 @@
+import prisma from "@/utils/prisma";
+
+export async function getAiUserByEmail({ email }: { email: string }) {
+ return prisma.user.findUnique({
+ where: { email },
+ select: {
+ id: true,
+ email: true,
+ about: true,
+ aiProvider: true,
+ aiModel: true,
+ aiApiKey: true,
+ },
+ });
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 89e4455b7d..baeb5eec7e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -36,17 +36,17 @@ importers:
apps/unsubscriber:
dependencies:
'@ai-sdk/amazon-bedrock':
- specifier: ^1.0.6
- version: 1.0.6(zod@3.24.1)
+ specifier: 1.1.6
+ version: 1.1.6(zod@3.24.1)
'@ai-sdk/anthropic':
- specifier: ^1.0.8
- version: 1.0.8(zod@3.24.1)
+ specifier: 1.1.6
+ version: 1.1.6(zod@3.24.1)
'@ai-sdk/google':
- specifier: ^1.0.12
- version: 1.0.12(zod@3.24.1)
+ specifier: 1.1.11
+ version: 1.1.11(zod@3.24.1)
'@ai-sdk/openai':
- specifier: ^1.0.11
- version: 1.0.11(zod@3.24.1)
+ specifier: 1.1.9
+ version: 1.1.9(zod@3.24.1)
'@fastify/cors':
specifier: ^10.0.1
version: 10.0.1
@@ -54,8 +54,8 @@ importers:
specifier: ^0.11.1
version: 0.11.1(typescript@5.7.2)(zod@3.24.1)
ai:
- specifier: ^4.0.31
- version: 4.0.31(react@18.3.1)(zod@3.24.1)
+ specifier: 4.1.32
+ version: 4.1.32(react@18.3.1)(zod@3.24.1)
dotenv:
specifier: ^16.4.7
version: 16.4.7
@@ -85,20 +85,20 @@ importers:
apps/web:
dependencies:
'@ai-sdk/amazon-bedrock':
- specifier: ^1.0.6
- version: 1.0.6(zod@3.24.1)
+ specifier: 1.1.6
+ version: 1.1.6(zod@3.24.1)
'@ai-sdk/anthropic':
- specifier: ^1.0.8
- version: 1.0.8(zod@3.24.1)
+ specifier: 1.1.6
+ version: 1.1.6(zod@3.24.1)
'@ai-sdk/google':
- specifier: ^1.0.12
- version: 1.0.12(zod@3.24.1)
+ specifier: 1.1.11
+ version: 1.1.11(zod@3.24.1)
'@ai-sdk/groq':
- specifier: ^1.0.10
- version: 1.0.10(zod@3.24.1)
+ specifier: 1.1.7
+ version: 1.1.7(zod@3.24.1)
'@ai-sdk/openai':
- specifier: ^1.0.11
- version: 1.0.11(zod@3.24.1)
+ specifier: 1.1.9
+ version: 1.1.9(zod@3.24.1)
'@asteasolutions/zod-to-openapi':
specifier: ^7.3.0
version: 7.3.0(zod@3.24.1)
@@ -224,7 +224,7 @@ importers:
version: 1.0.2
'@sanity/vision':
specifier: '3'
- version: 3.56.0(@babel/runtime@7.24.1)(@codemirror/lint@6.8.1)(@codemirror/theme-one-dark@6.1.2)(@lezer/common@1.2.1)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
+ version: 3.56.0(@babel/runtime@7.24.1)(@codemirror/lint@6.8.1)(@codemirror/theme-one-dark@6.1.2)(@lezer/common@1.2.1)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
'@sentry/nextjs':
specifier: ^8.47.0
version: 8.47.0(@opentelemetry/core@1.29.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.29.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.90.3(@swc/core@1.6.5(@swc/helpers@0.5.13))(esbuild@0.21.5))
@@ -268,8 +268,8 @@ importers:
specifier: ^1.4.1
version: 1.4.1(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(svelte@4.2.12)(vue@3.4.19(typescript@5.7.2))
ai:
- specifier: ^4.0.31
- version: 4.0.31(react@18.3.1)(zod@3.24.1)
+ specifier: 4.1.32
+ version: 4.1.32(react@18.3.1)(zod@3.24.1)
capital-case:
specifier: ^2.0.0
version: 2.0.0
@@ -350,7 +350,7 @@ importers:
version: 1.9.1(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
next-sanity:
specifier: '9'
- version: 9.4.7(@sanity/client@6.24.1)(@sanity/icons@3.5.6(react@18.3.1))(@sanity/types@3.68.3(@types/react@18.3.12))(@sanity/ui@2.10.12(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sanity@3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.37.0))(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(svelte@4.2.12)
+ version: 9.4.7(@sanity/client@6.24.1)(@sanity/icons@3.5.6(react@18.3.1))(@sanity/types@3.68.3(@types/react@18.3.12))(@sanity/ui@2.10.12(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sanity@3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.38.1))(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(svelte@4.2.12)
nodemailer:
specifier: ^6.9.16
version: 6.9.16
@@ -410,7 +410,7 @@ importers:
version: 10.1.0(react@18.3.1)
sanity:
specifier: ^3.68.3
- version: 3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.37.0)
+ version: 3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.38.1)
server-only:
specifier: ^0.0.1
version: 0.0.1
@@ -519,13 +519,13 @@ importers:
version: link:../../packages/tsconfig
vite-tsconfig-paths:
specifier: ^5.1.4
- version: 5.1.4(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.2)(terser@5.37.0))
+ version: 5.1.4(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.2)(terser@5.38.1))
vitest:
specifier: 2.1.8
- version: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0)
+ version: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1)
vitest-mock-extended:
specifier: ^2.0.2
- version: 2.0.2(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0))
+ version: 2.0.2(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1))
packages/eslint-config:
devDependencies:
@@ -537,7 +537,7 @@ importers:
version: 8.18.2(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2)
'@vercel/style-guide':
specifier: ^6.0.0
- version: 6.0.0(@next/eslint-plugin-next@14.2.15)(eslint@9.10.0(jiti@2.4.2))(prettier@3.4.2)(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0))
+ version: 6.0.0(@next/eslint-plugin-next@14.2.15)(eslint@9.10.0(jiti@2.4.2))(prettier@3.4.2)(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1))
eslint-config-prettier:
specifier: ^9.1.0
version: 9.1.0(eslint@9.10.0(jiti@2.4.2))
@@ -671,32 +671,32 @@ packages:
'@actions/io@1.1.3':
resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==}
- '@ai-sdk/amazon-bedrock@1.0.6':
- resolution: {integrity: sha512-EdbLjy/r9W6ds5/xbkfklr5C9y3PmGh2eXqhd3xyURq0oSoB9ukoOa9jvPTb4b3jS6l4R7yXYJvTZiAkkefUeQ==}
+ '@ai-sdk/amazon-bedrock@1.1.6':
+ resolution: {integrity: sha512-h6SJWpku+i8OsSz0A4RT2g2uD+3E0SUgWHsWRIpxmPNgM1DnH6lgSby5sxqAZDY5xJyJtRFW5vB9G3GEBjHy/g==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
- '@ai-sdk/anthropic@1.0.8':
- resolution: {integrity: sha512-SruTs0JOZ5ZnVV2hzeu0XDzRrT9WHcgx9P1p5vpjJFJVr9FlVaTxgxisL+8tlhZy8FX68zAhtj09rAaL4gT+jA==}
+ '@ai-sdk/anthropic@1.1.6':
+ resolution: {integrity: sha512-4TZBg2VoU/F58DmnyfPPGU9wMUTwLP15XyAFSrUqk9sSdjszwcojXw3LE7YbxifZ+RK7wT7lTkuyK1k2UdfFng==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
- '@ai-sdk/google@1.0.12':
- resolution: {integrity: sha512-vZUK8X997tKmycwCa9d26PoGtIyNEILykYb6JscMoA/pfr5Nss8Ox1JtSGn+PRkehpJhclOaLNWV1JQAjp73aA==}
+ '@ai-sdk/google@1.1.11':
+ resolution: {integrity: sha512-EcK20MTA3zNJKNOo3r52Y0N960lGL6UxUimt13HFk2RJ4dXPMWl7ZhWFgjwFXwW2QwdSPKqlMHYjne3xvKTBcQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
- '@ai-sdk/groq@1.0.10':
- resolution: {integrity: sha512-FU2UT0+cz2VsaI7M0JgynC/3tXLspBBPBMauV36FfkFSeHscI1CCQlzQoTngp4mqXbfD+tJFN8gEg8qYCkawzw==}
+ '@ai-sdk/groq@1.1.7':
+ resolution: {integrity: sha512-OavkZPF42QcJUltw8N/AXmRJvHBCf+I3Nx0FFywzN8xanEEtHothdMv6qDn0nwta+5itd+DEPI+/tLTIplmsMw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
- '@ai-sdk/openai@1.0.11':
- resolution: {integrity: sha512-qI9s7Slma5i5bB4yYVlFdcG3PNDwdqivPT1Dr8adDX92nSSpILjgFIooS5yys9sXjvvcfOi/WXbDvVhLSRRlvg==}
+ '@ai-sdk/openai@1.1.9':
+ resolution: {integrity: sha512-t/CpC4TLipdbgBJTMX/otzzqzCMBSPQwUOkYPGbT/jyuC86F+YO9o+LS0Ty2pGUE1kyT+B3WmJ318B16ZCg4hw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
@@ -710,26 +710,8 @@ packages:
zod:
optional: true
- '@ai-sdk/provider-utils@2.0.5':
- resolution: {integrity: sha512-2M7vLhYN0ThGjNlzow7oO/lsL+DyMxvGMIYmVQvEYaCWhDzxH5dOp78VNjJIVwHzVLMbBDigX3rJuzAs853idw==}
- engines: {node: '>=18'}
- peerDependencies:
- zod: ^3.0.0
- peerDependenciesMeta:
- zod:
- optional: true
-
- '@ai-sdk/provider-utils@2.0.6':
- resolution: {integrity: sha512-nB0rPwIBSCk0UkfdkprAxQ45ZjfKlk+Ts5zvIBQkJ5SnTCL9meg6bW65aomQrxhdvtqZML2jjaWTI8/l6AIVlQ==}
- engines: {node: '>=18'}
- peerDependencies:
- zod: ^3.0.0
- peerDependenciesMeta:
- zod:
- optional: true
-
- '@ai-sdk/provider-utils@2.0.7':
- resolution: {integrity: sha512-4sfPlKEALHPXLmMFcPlYksst3sWBJXmCDZpIBJisRrmwGG6Nn3mq0N1Zu/nZaGcrWZoOY+HT2Wbxla1oTElYHQ==}
+ '@ai-sdk/provider-utils@2.1.6':
+ resolution: {integrity: sha512-Pfyaj0QZS22qyVn5Iz7IXcJ8nKIKlu2MeSAdKJzTwkAks7zdLaKVB+396Rqcp1bfQnxl7vaduQVMQiXUrgK8Gw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
@@ -741,16 +723,12 @@ packages:
resolution: {integrity: sha512-YYtP6xWQyaAf5LiWLJ+ycGTOeBLWrED7LUrvc+SQIWhGaneylqbaGsyQL7VouQUeQ4JZ1qKYZuhmi3W56HADPA==}
engines: {node: '>=18'}
- '@ai-sdk/provider@1.0.3':
- resolution: {integrity: sha512-WiuJEpHTrltOIzv3x2wx4gwksAHW0h6nK3SoDzjqCOJLu/2OJ1yASESTIX+f07ChFykHElVoP80Ol/fe9dw6tQ==}
- engines: {node: '>=18'}
-
- '@ai-sdk/provider@1.0.4':
- resolution: {integrity: sha512-lJi5zwDosvvZER3e/pB8lj1MN3o3S7zJliQq56BRr4e9V3fcRyFtwP0JRxaRS5vHYX3OJ154VezVoQNrk0eaKw==}
+ '@ai-sdk/provider@1.0.7':
+ resolution: {integrity: sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g==}
engines: {node: '>=18'}
- '@ai-sdk/react@1.0.9':
- resolution: {integrity: sha512-7mtkgVCSzp8J4x3qk5Vtlk1FiZTH7vWIZvIrA6ISbFDy+7mwm45rIDIymzCiofzr3c/Wioy41H2Ki3Nth55bgg==}
+ '@ai-sdk/react@1.1.11':
+ resolution: {integrity: sha512-vfjZ7w2M+Me83HTMMrnnrmXotz39UDCMd27YQSrvt2f1YCLPloVpLhP+Y9TLZeFE/QiiRCrPYLDQm6aQJYJ9PQ==}
engines: {node: '>=18'}
peerDependencies:
react: ^18 || ^19 || ^19.0.0-rc
@@ -761,8 +739,8 @@ packages:
zod:
optional: true
- '@ai-sdk/ui-utils@1.0.8':
- resolution: {integrity: sha512-7ya/t28oMaFauHxSj4WGQCEV/iicZj9qP+O+tCakMIDq7oDCZMUNBLCQomoWs16CcYY4l0wo1S9hA4PAdFcOvA==}
+ '@ai-sdk/ui-utils@1.1.11':
+ resolution: {integrity: sha512-1SC9W4VZLcJtxHRv4Y0aX20EFeaEP6gUvVqoKLBBtMLOgtcZrv/F/HQRjGavGugiwlS3dsVza4X+E78fiwtlTA==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
@@ -1189,6 +1167,11 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
+ '@babel/parser@7.26.8':
+ resolution: {integrity: sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3':
resolution: {integrity: sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==}
engines: {node: '>=6.9.0'}
@@ -1771,6 +1754,10 @@ packages:
resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==}
engines: {node: '>=6.9.0'}
+ '@babel/types@7.26.8':
+ resolution: {integrity: sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==}
+ engines: {node: '>=6.9.0'}
+
'@biomejs/biome@1.9.4':
resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==}
engines: {node: '>=14.21.3'}
@@ -1835,19 +1822,14 @@ packages:
'@codemirror/view': ^6.0.0
'@lezer/common': ^1.0.0
- '@codemirror/autocomplete@6.18.3':
- resolution: {integrity: sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==}
- peerDependencies:
- '@codemirror/language': ^6.0.0
- '@codemirror/state': ^6.0.0
- '@codemirror/view': ^6.0.0
- '@lezer/common': ^1.0.0
+ '@codemirror/autocomplete@6.18.4':
+ resolution: {integrity: sha512-sFAphGQIqyQZfP2ZBsSHV7xQvo9Py0rV0dW7W3IMRdS+zDuNb2l3no78CvUaWKGfzFjI4FTrLdUSj86IGb2hRA==}
'@codemirror/commands@6.6.1':
resolution: {integrity: sha512-iBfKbyIoXS1FGdsKcZmnrxmbc8VcbMrSgD7AVrsnX+WyAYjmUDWvE93dt5D874qS4CCVu4O1JpbagHdXbbLiOw==}
- '@codemirror/commands@6.7.1':
- resolution: {integrity: sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==}
+ '@codemirror/commands@6.8.0':
+ resolution: {integrity: sha512-q8VPEFaEP4ikSlt6ZxjB3zW72+7osfAYW9i8Zu943uqbKuz6utc1+F170hyLUCUltXORjQXRyYQNfkckzA/bPQ==}
'@codemirror/lang-javascript@6.2.2':
resolution: {integrity: sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==}
@@ -1855,8 +1837,8 @@ packages:
'@codemirror/language@6.10.2':
resolution: {integrity: sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==}
- '@codemirror/language@6.10.6':
- resolution: {integrity: sha512-KrsbdCnxEztLVbB5PycWXFxas4EOyk/fPAfruSOnDDppevQgid2XZ+KbJ9u+fDikP/e7MW7HPBTvTb8JlZK9vA==}
+ '@codemirror/language@6.10.8':
+ resolution: {integrity: sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==}
'@codemirror/lint@6.8.1':
resolution: {integrity: sha512-IZ0Y7S4/bpaunwggW2jYqwLuHj0QtESf5xcROewY6+lDNwZ/NzvR4t+vpYgg9m7V8UXLPYqG+lu3DF470E5Oxg==}
@@ -1870,8 +1852,8 @@ packages:
'@codemirror/state@6.4.1':
resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==}
- '@codemirror/state@6.5.0':
- resolution: {integrity: sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==}
+ '@codemirror/state@6.5.2':
+ resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==}
'@codemirror/theme-one-dark@6.1.2':
resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==}
@@ -1879,8 +1861,8 @@ packages:
'@codemirror/view@6.33.0':
resolution: {integrity: sha512-AroaR3BvnjRW8fiZBalAaK+ZzB5usGgI014YKElYZvQdNH5ZIidHlO+cyf/2rWzyBFRkvG6VhiXeAEbC53P2YQ==}
- '@codemirror/view@6.35.3':
- resolution: {integrity: sha512-ScY7L8+EGdPl4QtoBiOzE4FELp7JmNUsBvgBcCakXWM2uiv/K89VAzU3BMDscf0DsACLvTKePbd5+cFDTcei6g==}
+ '@codemirror/view@6.36.2':
+ resolution: {integrity: sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==}
'@cspotcode/source-map-support@0.8.1':
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
@@ -2548,8 +2530,8 @@ packages:
resolution: {integrity: sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/object-schema@2.1.5':
- resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==}
+ '@eslint/object-schema@2.1.6':
+ resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/plugin-kit@0.1.0':
@@ -5938,8 +5920,8 @@ packages:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
- ai@4.0.31:
- resolution: {integrity: sha512-+ZgOBNQAnv/liKnA/By0716cOwWjQ2UGOvWPHpqJBUL1x02YirENc0sLf8CA+gysMBJflqPKzRdv7px4NVrPUg==}
+ ai@4.1.32:
+ resolution: {integrity: sha512-+5nC5TXFhkANXoBAHMGtR7uBUdMJVA52kguAC4oovKiJdBCbcHPxAAN9f/YCZvhQ2Pcn3A0hwTRuK55yBPJsxw==}
engines: {node: '>=18'}
peerDependencies:
react: ^18 || ^19 || ^19.0.0-rc
@@ -7088,8 +7070,8 @@ packages:
resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==}
engines: {node: '>=10.13.0'}
- enhanced-resolve@5.17.1:
- resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
+ enhanced-resolve@5.18.1:
+ resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
engines: {node: '>=10.13.0'}
entities@4.5.0:
@@ -7122,6 +7104,9 @@ packages:
es-module-lexer@1.5.4:
resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
+ es-module-lexer@1.6.0:
+ resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
+
es-object-atoms@1.0.0:
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
engines: {node: '>= 0.4'}
@@ -8112,6 +8097,10 @@ packages:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
import-in-the-middle@1.11.2:
resolution: {integrity: sha512-gK6Rr6EykBcc6cVWRSBR5TWf8nn6hZMYSRYqCcHa0l0d1fPK7JSYo6+Mlmck76jIX9aL/IZ71c06U2VpFwl1zA==}
@@ -11086,6 +11075,11 @@ packages:
engines: {node: '>=10'}
hasBin: true
+ terser@5.38.1:
+ resolution: {integrity: sha512-GWANVlPM/ZfYzuPHjq0nxT+EbOEDDN3Jwhwdg1D8TU8oSkktp8w64Uq4auuGLxFSoNTRDncTq2hQHX1Ld9KHkA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
text-decoder@1.1.1:
resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==}
@@ -12060,37 +12054,37 @@ snapshots:
'@actions/io@1.1.3': {}
- '@ai-sdk/amazon-bedrock@1.0.6(zod@3.24.1)':
+ '@ai-sdk/amazon-bedrock@1.1.6(zod@3.24.1)':
dependencies:
- '@ai-sdk/provider': 1.0.3
- '@ai-sdk/provider-utils': 2.0.5(zod@3.24.1)
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
'@aws-sdk/client-bedrock-runtime': 3.669.0
zod: 3.24.1
transitivePeerDependencies:
- aws-crt
- '@ai-sdk/anthropic@1.0.8(zod@3.24.1)':
+ '@ai-sdk/anthropic@1.1.6(zod@3.24.1)':
dependencies:
- '@ai-sdk/provider': 1.0.4
- '@ai-sdk/provider-utils': 2.0.7(zod@3.24.1)
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
zod: 3.24.1
- '@ai-sdk/google@1.0.12(zod@3.24.1)':
+ '@ai-sdk/google@1.1.11(zod@3.24.1)':
dependencies:
- '@ai-sdk/provider': 1.0.3
- '@ai-sdk/provider-utils': 2.0.5(zod@3.24.1)
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
zod: 3.24.1
- '@ai-sdk/groq@1.0.10(zod@3.24.1)':
+ '@ai-sdk/groq@1.1.7(zod@3.24.1)':
dependencies:
- '@ai-sdk/provider': 1.0.4
- '@ai-sdk/provider-utils': 2.0.6(zod@3.24.1)
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
zod: 3.24.1
- '@ai-sdk/openai@1.0.11(zod@3.24.1)':
+ '@ai-sdk/openai@1.1.9(zod@3.24.1)':
dependencies:
- '@ai-sdk/provider': 1.0.3
- '@ai-sdk/provider-utils': 2.0.5(zod@3.24.1)
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
zod: 3.24.1
'@ai-sdk/provider-utils@2.0.4(zod@3.24.1)':
@@ -12102,27 +12096,9 @@ snapshots:
optionalDependencies:
zod: 3.24.1
- '@ai-sdk/provider-utils@2.0.5(zod@3.24.1)':
- dependencies:
- '@ai-sdk/provider': 1.0.3
- eventsource-parser: 3.0.0
- nanoid: 3.3.8
- secure-json-parse: 2.7.0
- optionalDependencies:
- zod: 3.24.1
-
- '@ai-sdk/provider-utils@2.0.6(zod@3.24.1)':
+ '@ai-sdk/provider-utils@2.1.6(zod@3.24.1)':
dependencies:
- '@ai-sdk/provider': 1.0.4
- eventsource-parser: 3.0.0
- nanoid: 3.3.8
- secure-json-parse: 2.7.0
- optionalDependencies:
- zod: 3.24.1
-
- '@ai-sdk/provider-utils@2.0.7(zod@3.24.1)':
- dependencies:
- '@ai-sdk/provider': 1.0.4
+ '@ai-sdk/provider': 1.0.7
eventsource-parser: 3.0.0
nanoid: 3.3.8
secure-json-parse: 2.7.0
@@ -12133,28 +12109,24 @@ snapshots:
dependencies:
json-schema: 0.4.0
- '@ai-sdk/provider@1.0.3':
+ '@ai-sdk/provider@1.0.7':
dependencies:
json-schema: 0.4.0
- '@ai-sdk/provider@1.0.4':
+ '@ai-sdk/react@1.1.11(react@18.3.1)(zod@3.24.1)':
dependencies:
- json-schema: 0.4.0
-
- '@ai-sdk/react@1.0.9(react@18.3.1)(zod@3.24.1)':
- dependencies:
- '@ai-sdk/provider-utils': 2.0.7(zod@3.24.1)
- '@ai-sdk/ui-utils': 1.0.8(zod@3.24.1)
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
+ '@ai-sdk/ui-utils': 1.1.11(zod@3.24.1)
swr: 2.3.0(react@18.3.1)
throttleit: 2.1.0
optionalDependencies:
react: 18.3.1
zod: 3.24.1
- '@ai-sdk/ui-utils@1.0.8(zod@3.24.1)':
+ '@ai-sdk/ui-utils@1.1.11(zod@3.24.1)':
dependencies:
- '@ai-sdk/provider': 1.0.4
- '@ai-sdk/provider-utils': 2.0.7(zod@3.24.1)
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
zod-to-json-schema: 3.24.1(zod@3.24.1)
optionalDependencies:
zod: 3.24.1
@@ -12981,6 +12953,11 @@ snapshots:
dependencies:
'@babel/types': 7.26.3
+ '@babel/parser@7.26.8':
+ dependencies:
+ '@babel/types': 7.26.8
+ optional: true
+
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
@@ -13747,6 +13724,12 @@ snapshots:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
+ '@babel/types@7.26.8':
+ dependencies:
+ '@babel/helper-string-parser': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+ optional: true
+
'@biomejs/biome@1.9.4':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 1.9.4
@@ -13793,11 +13776,11 @@ snapshots:
'@codemirror/view': 6.33.0
'@lezer/common': 1.2.1
- '@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.1)':
+ '@codemirror/autocomplete@6.18.4':
dependencies:
- '@codemirror/language': 6.10.6
- '@codemirror/state': 6.5.0
- '@codemirror/view': 6.35.3
+ '@codemirror/language': 6.10.8
+ '@codemirror/state': 6.5.2
+ '@codemirror/view': 6.36.2
'@lezer/common': 1.2.1
'@codemirror/commands@6.6.1':
@@ -13807,11 +13790,11 @@ snapshots:
'@codemirror/view': 6.33.0
'@lezer/common': 1.2.1
- '@codemirror/commands@6.7.1':
+ '@codemirror/commands@6.8.0':
dependencies:
- '@codemirror/language': 6.10.6
- '@codemirror/state': 6.5.0
- '@codemirror/view': 6.35.3
+ '@codemirror/language': 6.10.8
+ '@codemirror/state': 6.5.2
+ '@codemirror/view': 6.36.2
'@lezer/common': 1.2.1
'@codemirror/lang-javascript@6.2.2':
@@ -13833,10 +13816,10 @@ snapshots:
'@lezer/lr': 1.4.2
style-mod: 4.1.2
- '@codemirror/language@6.10.6':
+ '@codemirror/language@6.10.8':
dependencies:
- '@codemirror/state': 6.5.0
- '@codemirror/view': 6.35.3
+ '@codemirror/state': 6.5.2
+ '@codemirror/view': 6.36.2
'@lezer/common': 1.2.1
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
@@ -13856,21 +13839,21 @@ snapshots:
'@codemirror/search@6.5.8':
dependencies:
- '@codemirror/state': 6.5.0
- '@codemirror/view': 6.35.3
+ '@codemirror/state': 6.5.2
+ '@codemirror/view': 6.36.2
crelt: 1.0.6
'@codemirror/state@6.4.1': {}
- '@codemirror/state@6.5.0':
+ '@codemirror/state@6.5.2':
dependencies:
'@marijn/find-cluster-break': 1.0.2
'@codemirror/theme-one-dark@6.1.2':
dependencies:
- '@codemirror/language': 6.10.6
- '@codemirror/state': 6.5.0
- '@codemirror/view': 6.35.3
+ '@codemirror/language': 6.10.8
+ '@codemirror/state': 6.5.2
+ '@codemirror/view': 6.36.2
'@lezer/highlight': 1.2.1
'@codemirror/view@6.33.0':
@@ -13879,9 +13862,9 @@ snapshots:
style-mod: 4.1.2
w3c-keyname: 2.2.8
- '@codemirror/view@6.35.3':
+ '@codemirror/view@6.36.2':
dependencies:
- '@codemirror/state': 6.5.0
+ '@codemirror/state': 6.5.2
style-mod: 4.1.2
w3c-keyname: 2.2.8
@@ -14255,7 +14238,7 @@ snapshots:
'@eslint/config-array@0.18.0':
dependencies:
- '@eslint/object-schema': 2.1.5
+ '@eslint/object-schema': 2.1.6
debug: 4.4.0
minimatch: 3.1.2
transitivePeerDependencies:
@@ -14282,7 +14265,7 @@ snapshots:
espree: 10.3.0
globals: 14.0.0
ignore: 5.3.2
- import-fresh: 3.3.0
+ import-fresh: 3.3.1
js-yaml: 4.1.0
minimatch: 3.1.2
strip-json-comments: 3.1.1
@@ -14293,7 +14276,7 @@ snapshots:
'@eslint/js@9.10.0': {}
- '@eslint/object-schema@2.1.5': {}
+ '@eslint/object-schema@2.1.6': {}
'@eslint/plugin-kit@0.1.0':
dependencies:
@@ -16511,7 +16494,7 @@ snapshots:
'@types/uuid': 8.3.4
uuid: 8.3.2
- '@sanity/vision@3.56.0(@babel/runtime@7.24.1)(@codemirror/lint@6.8.1)(@codemirror/theme-one-dark@6.1.2)(@lezer/common@1.2.1)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
+ '@sanity/vision@3.56.0(@babel/runtime@7.24.1)(@codemirror/lint@6.8.1)(@codemirror/theme-one-dark@6.1.2)(@lezer/common@1.2.1)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
dependencies:
'@codemirror/autocomplete': 6.18.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.33.0)(@lezer/common@1.2.1)
'@codemirror/commands': 6.6.1
@@ -16527,7 +16510,7 @@ snapshots:
'@sanity/color': 3.0.6
'@sanity/icons': 3.5.6(react@18.3.1)
'@sanity/ui': 2.8.9(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
- '@uiw/react-codemirror': 4.23.0(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.33.0)(@lezer/common@1.2.1))(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.33.0)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@uiw/react-codemirror': 4.23.0(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.33.0)(@lezer/common@1.2.1))(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.33.0)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
is-hotkey-esm: 1.0.0
json-2-csv: 5.5.5
json5: 2.2.3
@@ -18013,7 +17996,7 @@ snapshots:
'@codemirror/state': 6.4.1
'@codemirror/view': 6.33.0
- '@uiw/react-codemirror@4.23.0(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.33.0)(@lezer/common@1.2.1))(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.33.0)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@uiw/react-codemirror@4.23.0(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.33.0)(@lezer/common@1.2.1))(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.33.0)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.1
'@codemirror/commands': 6.6.1
@@ -18021,7 +18004,7 @@ snapshots:
'@codemirror/theme-one-dark': 6.1.2
'@codemirror/view': 6.33.0
'@uiw/codemirror-extensions-basic-setup': 4.23.0(@codemirror/autocomplete@6.18.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.33.0)(@lezer/common@1.2.1))(@codemirror/commands@6.6.1)(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.33.0)
- codemirror: 6.0.1(@lezer/common@1.2.1)
+ codemirror: 6.0.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
transitivePeerDependencies:
@@ -18051,7 +18034,7 @@ snapshots:
'@vercel/stega@0.1.2': {}
- '@vercel/style-guide@6.0.0(@next/eslint-plugin-next@14.2.15)(eslint@9.10.0(jiti@2.4.2))(prettier@3.4.2)(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0))':
+ '@vercel/style-guide@6.0.0(@next/eslint-plugin-next@14.2.15)(eslint@9.10.0(jiti@2.4.2))(prettier@3.4.2)(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1))':
dependencies:
'@babel/core': 7.24.7
'@babel/eslint-parser': 7.25.0(@babel/core@7.24.7)(eslint@9.10.0(jiti@2.4.2))
@@ -18071,7 +18054,7 @@ snapshots:
eslint-plugin-testing-library: 6.2.2(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2)
eslint-plugin-tsdoc: 0.2.17
eslint-plugin-unicorn: 51.0.1(eslint@9.10.0(jiti@2.4.2))
- eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0))
+ eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1))
prettier-plugin-packagejson: 2.5.1(prettier@3.4.2)
optionalDependencies:
'@next/eslint-plugin-next': 14.2.15
@@ -18085,14 +18068,14 @@ snapshots:
- supports-color
- vitest
- '@vitejs/plugin-react@4.3.4(vite@5.4.11(@types/node@22.10.2)(terser@5.37.0))':
+ '@vitejs/plugin-react@4.3.4(vite@5.4.11(@types/node@22.10.2)(terser@5.38.1))':
dependencies:
'@babel/core': 7.26.0
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
- vite: 5.4.11(@types/node@22.10.2)(terser@5.37.0)
+ vite: 5.4.11(@types/node@22.10.2)(terser@5.38.1)
transitivePeerDependencies:
- supports-color
@@ -18103,13 +18086,13 @@ snapshots:
chai: 5.1.2
tinyrainbow: 1.2.0
- '@vitest/mocker@2.1.8(vite@5.1.3(@types/node@22.10.2)(terser@5.37.0))':
+ '@vitest/mocker@2.1.8(vite@5.1.3(@types/node@22.10.2)(terser@5.38.1))':
dependencies:
'@vitest/spy': 2.1.8
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
- vite: 5.1.3(@types/node@22.10.2)(terser@5.37.0)
+ vite: 5.1.3(@types/node@22.10.2)(terser@5.38.1)
'@vitest/pretty-format@2.1.8':
dependencies:
@@ -18138,7 +18121,7 @@ snapshots:
'@vue/compiler-core@3.4.19':
dependencies:
- '@babel/parser': 7.26.3
+ '@babel/parser': 7.26.8
'@vue/shared': 3.4.19
entities: 4.5.0
estree-walker: 2.0.2
@@ -18153,7 +18136,7 @@ snapshots:
'@vue/compiler-sfc@3.4.19':
dependencies:
- '@babel/parser': 7.26.3
+ '@babel/parser': 7.26.8
'@vue/compiler-core': 3.4.19
'@vue/compiler-dom': 3.4.19
'@vue/compiler-ssr': 3.4.19
@@ -18344,15 +18327,14 @@ snapshots:
clean-stack: 2.2.0
indent-string: 4.0.0
- ai@4.0.31(react@18.3.1)(zod@3.24.1):
+ ai@4.1.32(react@18.3.1)(zod@3.24.1):
dependencies:
- '@ai-sdk/provider': 1.0.4
- '@ai-sdk/provider-utils': 2.0.7(zod@3.24.1)
- '@ai-sdk/react': 1.0.9(react@18.3.1)(zod@3.24.1)
- '@ai-sdk/ui-utils': 1.0.8(zod@3.24.1)
+ '@ai-sdk/provider': 1.0.7
+ '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
+ '@ai-sdk/react': 1.1.11(react@18.3.1)(zod@3.24.1)
+ '@ai-sdk/ui-utils': 1.1.11(zod@3.24.1)
'@opentelemetry/api': 1.9.0
jsondiffpatch: 0.6.0
- zod-to-json-schema: 3.24.1(zod@3.24.1)
optionalDependencies:
react: 18.3.1
zod: 3.24.1
@@ -18980,17 +18962,15 @@ snapshots:
periscopic: 3.1.0
optional: true
- codemirror@6.0.1(@lezer/common@1.2.1):
+ codemirror@6.0.1:
dependencies:
- '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.1)
- '@codemirror/commands': 6.7.1
- '@codemirror/language': 6.10.6
+ '@codemirror/autocomplete': 6.18.4
+ '@codemirror/commands': 6.8.0
+ '@codemirror/language': 6.10.8
'@codemirror/lint': 6.8.1
'@codemirror/search': 6.5.8
- '@codemirror/state': 6.5.0
- '@codemirror/view': 6.35.3
- transitivePeerDependencies:
- - '@lezer/common'
+ '@codemirror/state': 6.5.2
+ '@codemirror/view': 6.36.2
collapse-white-space@2.1.0: {}
@@ -19555,7 +19535,7 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.2.1
- enhanced-resolve@5.17.1:
+ enhanced-resolve@5.18.1:
dependencies:
graceful-fs: 4.2.11
tapable: 2.2.1
@@ -19642,6 +19622,8 @@ snapshots:
es-module-lexer@1.5.4: {}
+ es-module-lexer@1.6.0: {}
+
es-object-atoms@1.0.0:
dependencies:
es-errors: 1.3.0
@@ -20116,13 +20098,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0)):
+ eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1)):
dependencies:
'@typescript-eslint/utils': 7.17.0(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2)
eslint: 9.10.0(jiti@2.4.2)
optionalDependencies:
'@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.10.0(jiti@2.4.2))(typescript@5.7.2)
- vitest: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0)
+ vitest: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1)
transitivePeerDependencies:
- supports-color
- typescript
@@ -21123,6 +21105,11 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
import-in-the-middle@1.11.2:
dependencies:
acorn: 8.14.0
@@ -22330,7 +22317,7 @@ snapshots:
use-deep-compare: 1.3.0(react@18.3.1)
whatwg-fetch: 3.6.20
- next-sanity@9.4.7(@sanity/client@6.24.1)(@sanity/icons@3.5.6(react@18.3.1))(@sanity/types@3.68.3(@types/react@18.3.12))(@sanity/ui@2.10.12(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sanity@3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.37.0))(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(svelte@4.2.12):
+ next-sanity@9.4.7(@sanity/client@6.24.1)(@sanity/icons@3.5.6(react@18.3.1))(@sanity/types@3.68.3(@types/react@18.3.12))(@sanity/ui@2.10.12(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sanity@3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.38.1))(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(svelte@4.2.12):
dependencies:
'@portabletext/react': 3.2.0(react@18.3.1)
'@sanity/client': 6.24.1(debug@4.4.0)
@@ -22343,7 +22330,7 @@ snapshots:
history: 5.3.0
next: 14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
- sanity: 3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.37.0)
+ sanity: 3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.38.1)
styled-components: 6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
transitivePeerDependencies:
- '@remix-run/react'
@@ -23863,7 +23850,7 @@ snapshots:
dependencies:
'@sanity/diff-match-patch': 3.1.1
- sanity@3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.37.0):
+ sanity@3.68.3(@emotion/is-prop-valid@1.2.2)(@types/babel__core@7.20.5)(@types/node@22.10.2)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.38.1):
dependencies:
'@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/modifiers': 6.0.1(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
@@ -23905,7 +23892,7 @@ snapshots:
'@types/speakingurl': 13.0.6
'@types/tar-stream': 3.1.3
'@types/use-sync-external-store': 0.0.6
- '@vitejs/plugin-react': 4.3.4(vite@5.4.11(@types/node@22.10.2)(terser@5.37.0))
+ '@vitejs/plugin-react': 4.3.4(vite@5.4.11(@types/node@22.10.2)(terser@5.38.1))
archiver: 7.0.1
arrify: 2.0.1
async-mutex: 0.4.1
@@ -23982,7 +23969,7 @@ snapshots:
use-effect-event: 1.0.2(react@18.3.1)
use-hot-module-reload: 2.0.0(react@18.3.1)
use-sync-external-store: 1.2.2(react@18.3.1)
- vite: 5.4.11(@types/node@22.10.2)(terser@5.37.0)
+ vite: 5.4.11(@types/node@22.10.2)(terser@5.38.1)
yargs: 17.7.2
transitivePeerDependencies:
- '@emotion/is-prop-valid'
@@ -24627,7 +24614,7 @@ snapshots:
jest-worker: 27.5.1
schema-utils: 4.3.0
serialize-javascript: 6.0.2
- terser: 5.37.0
+ terser: 5.38.1
webpack: 5.90.3(@swc/core@1.6.5(@swc/helpers@0.5.13))(esbuild@0.21.5)
optionalDependencies:
'@swc/core': 1.6.5(@swc/helpers@0.5.13)
@@ -24640,6 +24627,13 @@ snapshots:
commander: 2.20.3
source-map-support: 0.5.21
+ terser@5.38.1:
+ dependencies:
+ '@jridgewell/source-map': 0.3.6
+ acorn: 8.14.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
text-decoder@1.1.1:
dependencies:
b4a: 1.6.6
@@ -25198,13 +25192,13 @@ snapshots:
d3-time: 3.1.0
d3-timer: 3.0.1
- vite-node@2.1.8(@types/node@22.10.2)(terser@5.37.0):
+ vite-node@2.1.8(@types/node@22.10.2)(terser@5.38.1):
dependencies:
cac: 6.7.14
debug: 4.4.0
es-module-lexer: 1.5.4
pathe: 1.1.2
- vite: 5.1.3(@types/node@22.10.2)(terser@5.37.0)
+ vite: 5.1.3(@types/node@22.10.2)(terser@5.38.1)
transitivePeerDependencies:
- '@types/node'
- less
@@ -25215,18 +25209,18 @@ snapshots:
- supports-color
- terser
- vite-tsconfig-paths@5.1.4(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.2)(terser@5.37.0)):
+ vite-tsconfig-paths@5.1.4(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.2)(terser@5.38.1)):
dependencies:
debug: 4.4.0
globrex: 0.1.2
tsconfck: 3.1.0(typescript@5.7.2)
optionalDependencies:
- vite: 5.4.11(@types/node@22.10.2)(terser@5.37.0)
+ vite: 5.4.11(@types/node@22.10.2)(terser@5.38.1)
transitivePeerDependencies:
- supports-color
- typescript
- vite@5.1.3(@types/node@22.10.2)(terser@5.37.0):
+ vite@5.1.3(@types/node@22.10.2)(terser@5.38.1):
dependencies:
esbuild: 0.19.11
postcss: 8.4.49
@@ -25234,9 +25228,9 @@ snapshots:
optionalDependencies:
'@types/node': 22.10.2
fsevents: 2.3.3
- terser: 5.37.0
+ terser: 5.38.1
- vite@5.4.11(@types/node@22.10.2)(terser@5.37.0):
+ vite@5.4.11(@types/node@22.10.2)(terser@5.38.1):
dependencies:
esbuild: 0.21.5
postcss: 8.4.49
@@ -25244,18 +25238,18 @@ snapshots:
optionalDependencies:
'@types/node': 22.10.2
fsevents: 2.3.3
- terser: 5.37.0
+ terser: 5.38.1
- vitest-mock-extended@2.0.2(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0)):
+ vitest-mock-extended@2.0.2(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1)):
dependencies:
ts-essentials: 10.0.3(typescript@5.7.2)
typescript: 5.7.2
- vitest: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0)
+ vitest: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1)
- vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.37.0):
+ vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.38.1):
dependencies:
'@vitest/expect': 2.1.8
- '@vitest/mocker': 2.1.8(vite@5.1.3(@types/node@22.10.2)(terser@5.37.0))
+ '@vitest/mocker': 2.1.8(vite@5.1.3(@types/node@22.10.2)(terser@5.38.1))
'@vitest/pretty-format': 2.1.8
'@vitest/runner': 2.1.8
'@vitest/snapshot': 2.1.8
@@ -25271,8 +25265,8 @@ snapshots:
tinyexec: 0.3.1
tinypool: 1.0.1
tinyrainbow: 1.2.0
- vite: 5.1.3(@types/node@22.10.2)(terser@5.37.0)
- vite-node: 2.1.8(@types/node@22.10.2)(terser@5.37.0)
+ vite: 5.1.3(@types/node@22.10.2)(terser@5.38.1)
+ vite-node: 2.1.8(@types/node@22.10.2)(terser@5.38.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.10.2
@@ -25340,8 +25334,8 @@ snapshots:
acorn-import-assertions: 1.9.0(acorn@8.14.0)
browserslist: 4.24.3
chrome-trace-event: 1.0.4
- enhanced-resolve: 5.17.1
- es-module-lexer: 1.5.4
+ enhanced-resolve: 5.18.1
+ es-module-lexer: 1.6.0
eslint-scope: 5.1.1
events: 3.3.0
glob-to-regexp: 0.4.1