diff --git a/apps/app/package.json b/apps/app/package.json
index fc14589..b4a71b1 100644
--- a/apps/app/package.json
+++ b/apps/app/package.json
@@ -18,11 +18,6 @@
"check-types": "next typegen && tsc --noEmit"
},
"dependencies": {
- "@pilot/core": "workspace:*",
- "@pilot/db": "workspace:*",
- "@pilot/instagram": "workspace:*",
- "@pilot/types": "workspace:*",
- "@pilot/ui": "workspace:*",
"@ai-sdk/google": "^2.0.54",
"@ai-sdk/react": "^2.0.139",
"@hookform/resolvers": "^5.2.2",
@@ -30,6 +25,11 @@
"@neondatabase/serverless": "^1.0.2",
"@opentelemetry/winston-transport": "^0.16.2",
"@origin-space/image-cropper": "^0.1.9",
+ "@pilot/core": "workspace:*",
+ "@pilot/db": "workspace:*",
+ "@pilot/instagram": "workspace:*",
+ "@pilot/types": "workspace:*",
+ "@pilot/ui": "workspace:*",
"@polar-sh/better-auth": "^1.8.1",
"@polar-sh/nextjs": "^0.4.11",
"@polar-sh/sdk": "^0.34.17",
@@ -50,7 +50,7 @@
"dotenv": "^17.3.1",
"drizzle-orm": "^0.44.7",
"import-in-the-middle": "^2.0.6",
- "inngest": "^3.52.2",
+ "inngest": "^4.4.0",
"lucide-react": "^0.542.0",
"motion": "^12.34.3",
"next": "16.1.6",
diff --git a/apps/app/src/actions/sidekick/onboarding.ts b/apps/app/src/actions/sidekick/onboarding.ts
index 176f0c7..6234c46 100644
--- a/apps/app/src/actions/sidekick/onboarding.ts
+++ b/apps/app/src/actions/sidekick/onboarding.ts
@@ -14,6 +14,10 @@ import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { and, eq } from "drizzle-orm";
import { enqueueBusinessKnowledgeSync } from "@/lib/supermemory/events";
+import {
+ getSidekickSetupStatusByUserId,
+ SIDEKICK_SETUP_STEPS,
+} from "@pilot/core/sidekick/personalization";
export type SidekickOnboardingData = {
offerLinks?: {
@@ -39,7 +43,7 @@ export type SidekickOnboardingData = {
};
export async function updateSidekickOnboardingData(
- data: SidekickOnboardingData
+ data: SidekickOnboardingData,
) {
const session = await auth.api.getSession({
headers: await headers(),
@@ -69,8 +73,8 @@ export async function updateSidekickOnboardingData(
and(
eq(userOfferLink.userId, session.user.id),
eq(userOfferLink.type, link.type),
- eq(userOfferLink.url, link.url)
- )
+ eq(userOfferLink.url, link.url),
+ ),
);
if (existingLinks.length === 0) {
@@ -93,8 +97,8 @@ export async function updateSidekickOnboardingData(
and(
eq(userOffer.userId, session.user.id),
eq(userOffer.name, offer.name),
- eq(userOffer.content, offer.content)
- )
+ eq(userOffer.content, offer.content),
+ ),
);
if (existingOffers.length === 0) {
@@ -117,8 +121,8 @@ export async function updateSidekickOnboardingData(
.where(
and(
eq(userFaq.userId, session.user.id),
- eq(userFaq.question, faq.question)
- )
+ eq(userFaq.question, faq.question),
+ ),
);
if (existingFaqs.length === 0) {
@@ -195,7 +199,7 @@ export async function deleteOffer(offerId: string) {
await db
.delete(userOffer)
.where(
- and(eq(userOffer.id, offerId), eq(userOffer.userId, session.user.id))
+ and(eq(userOffer.id, offerId), eq(userOffer.userId, session.user.id)),
);
await enqueueBusinessKnowledgeSync(session.user.id, "deleteOffer");
@@ -228,8 +232,8 @@ export async function saveSidekickOfferLink(linkData: {
and(
eq(userOfferLink.url, linkData.url),
eq(userOfferLink.userId, session.user.id),
- eq(userOfferLink.type, linkData.type)
- )
+ eq(userOfferLink.type, linkData.type),
+ ),
);
if (existingLinks.length === 0) {
@@ -322,8 +326,8 @@ export async function saveSidekickOffer(offerData: {
and(
eq(userOffer.userId, session.user.id),
eq(userOffer.name, offerData.name),
- eq(userOffer.content, offerData.content)
- )
+ eq(userOffer.content, offerData.content),
+ ),
);
if (existingOffers.length === 0) {
@@ -336,10 +340,7 @@ export async function saveSidekickOffer(offerData: {
});
}
- await enqueueBusinessKnowledgeSync(
- session.user.id,
- "saveSidekickOffer",
- );
+ await enqueueBusinessKnowledgeSync(session.user.id, "saveSidekickOffer");
return { success: true };
} catch (error) {
@@ -582,22 +583,40 @@ export async function checkSidekickOnboardingStatus() {
try {
const db = await getRLSDb();
- const userData = await db
- .select({
- sidekick_onboarding_complete: user.sidekick_onboarding_complete,
- })
- .from(user)
- .where(eq(user.id, session.user.id))
- .then((res) => res[0]);
+ const result = await getSidekickSetupStatusByUserId(db, session.user.id);
+
+ if (result.success) {
+ return result.data;
+ }
return {
- sidekick_onboarding_complete:
- userData?.sidekick_onboarding_complete || false,
+ sidekick_onboarding_complete: false,
+ isReady: false,
+ resumeStep: 0,
+ resumeHref: "/sidekick-onboarding?step=0",
+ completedSteps: 0,
+ totalSteps: SIDEKICK_SETUP_STEPS.length,
+ missing: ["Sidekick setup data"],
+ steps: SIDEKICK_SETUP_STEPS.map((step) => ({
+ ...step,
+ complete: false,
+ })),
+ error: result.error,
};
} catch (error) {
console.error("Error checking onboarding status:", error);
return {
sidekick_onboarding_complete: false,
+ isReady: false,
+ resumeStep: 0,
+ resumeHref: "/sidekick-onboarding?step=0",
+ completedSteps: 0,
+ totalSteps: SIDEKICK_SETUP_STEPS.length,
+ missing: ["Sidekick setup data"],
+ steps: SIDEKICK_SETUP_STEPS.map((step) => ({
+ ...step,
+ complete: false,
+ })),
error: "Failed to check onboarding status",
};
}
diff --git a/apps/app/src/app/(dashboard)/(workspace)/automations/page.tsx b/apps/app/src/app/(dashboard)/(workspace)/automations/page.tsx
index 5cc89aa..3e75c17 100644
--- a/apps/app/src/app/(dashboard)/(workspace)/automations/page.tsx
+++ b/apps/app/src/app/(dashboard)/(workspace)/automations/page.tsx
@@ -1,6 +1,6 @@
import { Suspense } from "react";
import { Button } from "@pilot/ui/components/button";
-import { Plus } from "lucide-react";
+import { LockKeyhole, Plus } from "lucide-react";
import Link from "next/link";
import AutomationsList from "@/components/automations/list";
import AutomationsLogs from "@/components/automations/logs";
@@ -8,19 +8,43 @@ import { Skeleton } from "@pilot/ui/components/skeleton";
import { SidekickLayout } from "@/components/sidekick/layout";
import { getUser } from "@/lib/auth-utils";
import { getBillingStatus } from "@/lib/billing/enforce";
+import { getInstagramIntegration } from "@/actions/instagram";
+
+function InstagramLockedAutomations() {
+ return (
+
+
+
+ Automations are locked
+
+
+ Once Instagram is connected, Pilot can safely load automation rules,
+ logs, and create flows that send replies from the right account.
+
+
+ );
+}
export default async function AutomationsPage() {
- const user = await getUser();
- const billingStatus = user ? await getBillingStatus(user.id) : null;
+ const [user, instagram] = await Promise.all([
+ getUser(),
+ getInstagramIntegration(),
+ ]);
+ const billingStatus =
+ user && instagram.connected ? await getBillingStatus(user.id) : null;
const isFrozen = billingStatus?.flags.isStructurallyFrozen ?? false;
- const canCreateAutomation = billingStatus?.flags.canCreateAutomation ?? false;
+ const canCreateAutomation =
+ instagram.connected && (billingStatus?.flags.canCreateAutomation ?? false);
return (
Automations
-
+
Build automated replies for common DM and comment questions.
@@ -40,42 +64,48 @@ export default async function AutomationsPage() {
{isFrozen && (
-
- Your workspace is frozen because it is above the current plan cap. Existing automations remain visible, but changes are disabled until usage is reduced or the plan is upgraded.
+
+ Your workspace is frozen because it is above the current plan cap.
+ Existing automations remain visible, but changes are disabled until
+ usage is reduced or the plan is upgraded.
)}
-
-
-
-
-
-
-
-
-
-
+ {instagram.connected ? (
+
+
+
+
+
+
+
+
+
+
+
-
- }
- >
-
-
+ }
+ >
+
+
-
-
-
-
-
- }
- >
-
-
-
+
+
+
+
+
+ }
+ >
+
+
+
+ ) : (
+
+ )}
);
}
diff --git a/apps/app/src/app/(dashboard)/(workspace)/contacts/page.tsx b/apps/app/src/app/(dashboard)/(workspace)/contacts/page.tsx
index cfdf5a9..9c801f1 100644
--- a/apps/app/src/app/(dashboard)/(workspace)/contacts/page.tsx
+++ b/apps/app/src/app/(dashboard)/(workspace)/contacts/page.tsx
@@ -1,23 +1,50 @@
+import { getInstagramIntegration } from "@/actions/instagram";
import { fetchContacts } from "@/actions/contacts";
import ContactsTable from "@/components/contacts/contacts-table";
+import { LockKeyhole } from "lucide-react";
export const dynamic = "force-dynamic";
+function InstagramLockedContacts() {
+ return (
+
+
+
+ Contacts are locked
+
+
+ Once Instagram is connected, Pilot can safely fetch synced contacts,
+ notes, tags, and follow-up state.
+
+
+ );
+}
+
export default async function ContactsPage() {
+ const instagram = await getInstagramIntegration();
+
let contacts = null;
let hasError = false;
- try {
- contacts = await fetchContacts();
- } catch (error) {
- console.error("Error in ContactsPage:", error);
- hasError = true;
+
+ if (instagram.connected) {
+ try {
+ contacts = await fetchContacts();
+ } catch (error) {
+ console.error("Error in ContactsPage:", error);
+ hasError = true;
+ }
}
if (hasError) {
return (
-
Contacts
+
+ Contacts
+
Failed to load contacts. Please try again later.
@@ -29,13 +56,19 @@ export default async function ContactsPage() {
return (
-
Contacts
-
+
+ Contacts
+
+
Keep your leads, notes, tags, and follow-ups in one view.
-
+ {instagram.connected ? (
+
+ ) : (
+
+ )}
);
}
diff --git a/apps/app/src/app/(dashboard)/(workspace)/page.tsx b/apps/app/src/app/(dashboard)/(workspace)/page.tsx
index 938b5c0..980f265 100644
--- a/apps/app/src/app/(dashboard)/(workspace)/page.tsx
+++ b/apps/app/src/app/(dashboard)/(workspace)/page.tsx
@@ -1,29 +1,82 @@
-import { redirect } from "next/navigation";
+import { getInstagramIntegration } from "@/actions/instagram";
import { checkSidekickOnboardingStatus } from "@/actions/sidekick/onboarding";
import { getSidekickMemoryOverview } from "@/actions/sidekick/memory";
import { SidekickPanel } from "@/components/sidekick/sidekick-panel";
import { FollowUpList } from "@/components/sidekick/follow-up-list";
import { HRNList } from "@/components/sidekick/hrn-list";
import { SidekickLayout } from "@/components/sidekick/layout";
+import {
+ Alert,
+ AlertDescription,
+ AlertTitle,
+} from "@pilot/ui/components/alert";
+import { CheckCircle2, LockKeyhole } from "lucide-react";
export const dynamic = "force-dynamic";
-export default async function SidekickPage() {
- const { sidekick_onboarding_complete } =
- await checkSidekickOnboardingStatus();
+function SidekickReadyBanner() {
+ return (
+
+
+ Sidekick setup complete
+
+ Sidekick is ready to use with your current onboarding data.
+
+
+ );
+}
- if (!sidekick_onboarding_complete) {
- redirect("/sidekick-onboarding");
- }
+function InstagramLockedSidekick() {
+ return (
+
+
+
Sidekick is locked
+
+ Once Instagram is connected, Pilot can safely load Sidekick memory, HRN,
+ follow-ups, and chat tools for the right account.
+
+
+ );
+}
+
+function SidekickUnavailable() {
+ return (
+
+
+
+ Sidekick is locked for now
+
+
+ Finish the required onboarding data before using chat, follow-ups, HRN,
+ or AI replies. This keeps Sidekick from sending messages without enough
+ business context.
+
+
+ );
+}
+
+export default async function SidekickPage() {
+ const [onboardingStatus, instagram] = await Promise.all([
+ checkSidekickOnboardingStatus(),
+ getInstagramIntegration(),
+ ]);
let overview = null;
let hasError = false;
let overviewResult;
- try {
- overviewResult = await getSidekickMemoryOverview();
- } catch (error) {
- console.error("Error in SidekickPage:", error);
- hasError = true;
+ if (onboardingStatus.isReady && instagram.connected) {
+ try {
+ overviewResult = await getSidekickMemoryOverview();
+ } catch (error) {
+ console.error("Error in SidekickPage:", error);
+ hasError = true;
+ }
}
if (overviewResult && overviewResult.success && overviewResult.overview) {
overview = overviewResult.overview;
@@ -43,21 +96,34 @@ export default async function SidekickPage() {
return (
-
Sidekick
-
- Your AI assistant for Instagram DMs. It helps you reply faster, follow up on time, and keep conversations moving.
+
+ Sidekick
+
+
+ Your AI assistant for Instagram DMs. It helps you reply faster, follow
+ up on time, and keep conversations moving.
-
-
-
-
-
-
-
-
-
+ {onboardingStatus.isReady && instagram.connected ? (
+
+ ) : null}
+
+ {!instagram.connected ? (
+
+ ) : !onboardingStatus.isReady ? (
+
+ ) : (
+
+
+
+
+
+
+
+
+
+ )}
);
}
diff --git a/apps/app/src/app/(dashboard)/layout.tsx b/apps/app/src/app/(dashboard)/layout.tsx
index 597ed8d..c7c0a08 100644
--- a/apps/app/src/app/(dashboard)/layout.tsx
+++ b/apps/app/src/app/(dashboard)/layout.tsx
@@ -5,11 +5,60 @@ import { redirect, unstable_rethrow } from "next/navigation";
import { db } from "@pilot/db";
import { user } from "@pilot/db/schema";
import { eq } from "drizzle-orm";
+import {
+ Alert,
+ AlertDescription,
+ AlertTitle,
+} from "@pilot/ui/components/alert";
+import { Badge } from "@pilot/ui/components/badge";
+import { Button } from "@pilot/ui/components/button";
import { SidebarInset, SidebarProvider } from "@pilot/ui/components/sidebar";
import { AppSidebar } from "@/components/dashboard/sidebar";
import PageHeader from "@/components/dashboard/page-header";
import { SidekickToggle } from "@/components/sidekick/toggle";
import { SidekickProvider } from "@/components/sidekick/context";
+import { checkSidekickOnboardingStatus } from "@/actions/sidekick/onboarding";
+import { TriangleAlert } from "lucide-react";
+import Link from "next/link";
+
+function SidekickSetupBanner({
+ status,
+}: {
+ status: Awaited
>;
+}) {
+ if (status.isReady) {
+ return null;
+ }
+
+ const statusMessage =
+ status.missing.length > 0
+ ? `Missing: ${status.missing.join(", ")}.`
+ : "Finish the final setup step to turn Sidekick on.";
+
+ return (
+
+
+
+
+
+ Sidekick setup incomplete
+
+
+ Sidekick is unavailable until setup is complete. {statusMessage}
+
+
+
+
+ {status.completedSteps} / {status.totalSteps}
+
+
+ Resume setup
+
+
+
+
+ );
+}
export default async function DashboardLayout({
children,
@@ -45,6 +94,18 @@ export default async function DashboardLayout({
redirect("/onboarding");
}
+ let sidekickStatus: Awaited<
+ ReturnType
+ > | null = null;
+ let sidekickReady = false;
+ try {
+ sidekickStatus = await checkSidekickOnboardingStatus();
+ sidekickReady = sidekickStatus.isReady;
+ } catch (error) {
+ unstable_rethrow(error);
+ console.error("Failed to fetch Sidekick setup status:", error);
+ }
+
return (
+ {sidekickStatus ? (
+
+ ) : null}
{children}
-
+ {sidekickReady ? : null}
);
diff --git a/apps/app/src/app/(onboarding)/onboarding/page.tsx b/apps/app/src/app/(onboarding)/onboarding/page.tsx
index ef2104d..f1f0861 100644
--- a/apps/app/src/app/(onboarding)/onboarding/page.tsx
+++ b/apps/app/src/app/(onboarding)/onboarding/page.tsx
@@ -260,7 +260,8 @@ async function checkInstagramConnectionAction(
setInstagramConnection(result);
setStepValidationState((previousState) => ({
...previousState,
- 0: result.connected,
+ 0: true,
+ 1: true,
}));
if (result.connected) {
@@ -365,8 +366,8 @@ async function submitStep2Action(
return;
}
- toast.success("Quick setup complete. Next up: Sidekick.");
- router.push("/sidekick-onboarding");
+ toast.success("Quick setup complete.");
+ router.push("/");
} catch (error) {
console.error("Error submitting step 2:", error);
toast.error("Hmm, something's not right. Give it another shot?");
@@ -394,8 +395,8 @@ function OnboardingPageContent() {
const [stepValidationState, setStepValidationState] = useState<
Record
>({
- 0: false,
- 1: false,
+ 0: true,
+ 1: true,
2: false,
3: false,
4: false,
@@ -573,6 +574,15 @@ function OnboardingPageContent() {
window.location.href = "/api/auth/instagram?returnTo=/onboarding";
};
+ const handleSkipInstagram = () => {
+ setStepValidationState((previousState) => ({
+ ...previousState,
+ 0: true,
+ 1: true,
+ }));
+ setActiveStep(2);
+ };
+
const handleRefreshPreview = async () => {
setInstagramPreview(null);
setPreviewError(null);
@@ -662,12 +672,12 @@ function OnboardingPageContent() {
-
+
Turn your next DM into revenue in 2 minutes.
-
- Connect Instagram first so Pilot can show you what the
- product feels like before asking for setup details.
+
+ Connect Instagram now for a live preview, or skip it and
+ finish your basic setup first.
@@ -698,8 +708,18 @@ function OnboardingPageContent() {
Connect Instagram
-
- We'll only read DMs to train your AI.
+
+ Skip for now
+
+
+
+ You can connect Instagram later from Settings.
@@ -711,10 +731,10 @@ function OnboardingPageContent() {
-
+
Setting things up...
-
+
We're pulling recent Instagram context and showing
you how Sidekick would jump into a real thread.
@@ -727,6 +747,31 @@ function OnboardingPageContent() {
+ {!instagramConnection.connected ? (
+
+
+ Instagram is optional
+
+
+ Skip the live preview for now and finish your basic setup.
+ You can connect Instagram later from Settings.
+
+
+
+ Continue setup
+
+
+ Connect Instagram
+
+
+
+ ) : null}
+
{isPreparingPreview ? (
@@ -1360,7 +1405,7 @@ function OnboardingPageContent() {
diff --git a/apps/app/src/app/(onboarding)/sidekick-onboarding/page.tsx b/apps/app/src/app/(onboarding)/sidekick-onboarding/page.tsx
index 2be73ee..7a66a14 100644
--- a/apps/app/src/app/(onboarding)/sidekick-onboarding/page.tsx
+++ b/apps/app/src/app/(onboarding)/sidekick-onboarding/page.tsx
@@ -458,7 +458,7 @@ export default function SidekickOnboardingPage() {
return;
}
- if (status.sidekick_onboarding_complete) {
+ if (status.isReady) {
router.replace("/");
if (isMounted) {
setIsInitializing(false);
@@ -565,6 +565,17 @@ export default function SidekickOnboardingPage() {
2: initialFaqs.length > 0,
3: !!toneProfileResult?.success && !!toneProfileResult.data?.toneType,
});
+
+ const stepParam = new URLSearchParams(window.location.search).get("step");
+ const requestedStep = stepParam !== null ? Number(stepParam) : null;
+ const stepFromUrl =
+ requestedStep !== null &&
+ Number.isInteger(requestedStep) &&
+ requestedStep >= 0 &&
+ requestedStep <= 3
+ ? requestedStep
+ : null;
+ setActiveStep(stepFromUrl ?? status.resumeStep ?? 0);
setIsInitializing(false);
}
@@ -642,6 +653,10 @@ export default function SidekickOnboardingPage() {
setActiveStep((prev) => prev + 1);
};
+ const handleSkip = () => {
+ router.push("/");
+ };
+
const isStepValid = (step: number) => {
if (step === 1) {
return offers.length > 0 && hasStoredMainOffering;
@@ -662,6 +677,21 @@ export default function SidekickOnboardingPage() {
+
+
+
+ Set up Sidekick
+
+
+ Add the business details Sidekick needs before it can reply for
+ you.
+
+
+
+ Skip for now
+
+
+
{
diff --git a/apps/app/src/app/api/chat/route.ts b/apps/app/src/app/api/chat/route.ts
index 56bdd23..728727c 100644
--- a/apps/app/src/app/api/chat/route.ts
+++ b/apps/app/src/app/api/chat/route.ts
@@ -21,6 +21,7 @@ import { z } from "zod";
import {
DEFAULT_SIDEKICK_PROMPT,
getBusinessKnowledgeSnapshotByUserId,
+ getSidekickSetupStatusByUserId,
} from "@pilot/core/sidekick/personalization";
import {
getUserProfile,
@@ -86,6 +87,31 @@ export async function POST(req: Request) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
+ const setupStatusResult = await getSidekickSetupStatusByUserId(db, user.id);
+
+ if (!setupStatusResult.success) {
+ console.error(
+ "Failed to check Sidekick setup status:",
+ setupStatusResult.error,
+ );
+ return Response.json(
+ { error: "Unable to verify Sidekick setup status. Please try again." },
+ { status: 503 },
+ );
+ }
+
+ const setupStatus = setupStatusResult.data;
+
+ if (!setupStatus?.isReady) {
+ return Response.json(
+ {
+ error: "Complete Sidekick setup before using chat.",
+ resumeHref: setupStatus?.resumeHref || "/sidekick-onboarding?step=0",
+ },
+ { status: 403 },
+ );
+ }
+
try {
await assertBillingAllowed(user.id, "sidekick:chat");
} catch (error) {
@@ -128,25 +154,29 @@ export async function POST(req: Request) {
db,
user.id,
).catch(() => null);
- const [knowledgeProfile, workspaceProfile, knowledgeResults, workspaceResults] =
- await Promise.all([
- getMemoryProfile({
- containerTag: getKnowledgeContainerTag(user.id),
- q: latestUserText,
- }).catch(() => null),
- getMemoryProfile({
- containerTag: getWorkspaceContainerTag(user.id),
- q: latestUserText,
- }).catch(() => null),
- searchMemory({
- containerTag: getKnowledgeContainerTag(user.id),
- q: latestUserText,
- }).catch(() => []),
- searchMemory({
- containerTag: getWorkspaceContainerTag(user.id),
- q: latestUserText,
- }).catch(() => []),
- ]);
+ const [
+ knowledgeProfile,
+ workspaceProfile,
+ knowledgeResults,
+ workspaceResults,
+ ] = await Promise.all([
+ getMemoryProfile({
+ containerTag: getKnowledgeContainerTag(user.id),
+ q: latestUserText,
+ }).catch(() => null),
+ getMemoryProfile({
+ containerTag: getWorkspaceContainerTag(user.id),
+ q: latestUserText,
+ }).catch(() => null),
+ searchMemory({
+ containerTag: getKnowledgeContainerTag(user.id),
+ q: latestUserText,
+ }).catch(() => []),
+ searchMemory({
+ containerTag: getWorkspaceContainerTag(user.id),
+ q: latestUserText,
+ }).catch(() => []),
+ ]);
const knowledgeMemoryContext = formatMemoryContext({
title: "Business knowledge memory",
@@ -506,7 +536,9 @@ export async function POST(req: Request) {
await saveChatSession({ sessionId: id, messages });
console.log(`Saved ${messages.length} messages to session ${id}`);
- const lastUserMessage = [...messages].reverse().find((item) => item.role === "user");
+ const lastUserMessage = [...messages]
+ .reverse()
+ .find((item) => item.role === "user");
const lastAssistantMessage = [...messages]
.reverse()
.find((item) => item.role === "assistant");
diff --git a/apps/app/src/components/instagram-connection-banner.tsx b/apps/app/src/components/instagram-connection-banner.tsx
index 551f771..01e63bf 100644
--- a/apps/app/src/components/instagram-connection-banner.tsx
+++ b/apps/app/src/components/instagram-connection-banner.tsx
@@ -2,10 +2,14 @@
import { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
-import { Alert, AlertDescription, AlertTitle } from "@pilot/ui/components/alert";
-import { cn } from "@pilot/ui/lib/utils";
+import {
+ Alert,
+ AlertDescription,
+ AlertTitle,
+} from "@pilot/ui/components/alert";
+import { Button } from "@pilot/ui/components/button";
import Link from "next/link";
-import { ArrowRight } from "lucide-react";
+import { ArrowRight, Instagram } from "lucide-react";
type InstagramStatus = {
connected: boolean;
@@ -14,7 +18,7 @@ type InstagramStatus = {
async function loadInstagramStatus(
setStatus: (s: InstagramStatus) => void,
- signal: AbortSignal
+ signal: AbortSignal,
) {
try {
const res = await fetch("/api/auth/instagram/status", {
@@ -57,24 +61,31 @@ export default function InstagramConnectionBanner() {
return (
-
- Instagram not connected
-
-
- Connect your Instagram account in Settings to use automations and contact sync.
-
+
+
+
+
+ Instagram not connected
+
+
+ Connect Instagram to use automations and contact sync.
+
+
+
{!isOnSettings && (
-
- Go to Settings
-
+
+ Settings
+
+
)}
);
diff --git a/apps/app/src/lib/auth.ts b/apps/app/src/lib/auth.ts
index 711bda2..6b58c01 100644
--- a/apps/app/src/lib/auth.ts
+++ b/apps/app/src/lib/auth.ts
@@ -62,11 +62,10 @@ export const auth = betterAuth({
checkout({
products: checkoutProducts,
authenticatedUsersOnly: true,
- successUrl: "/sidekick-onboarding",
+ successUrl: "/",
}),
portal(),
],
}),
],
});
-
diff --git a/apps/app/src/lib/constants/sidekick-onboarding.ts b/apps/app/src/lib/constants/sidekick-onboarding.ts
index 226d8f2..2ca3b93 100644
--- a/apps/app/src/lib/constants/sidekick-onboarding.ts
+++ b/apps/app/src/lib/constants/sidekick-onboarding.ts
@@ -1,20 +1,5 @@
-export const sidekickSteps = [
- {
- id: 0,
- name: "Your Links",
- },
- {
- id: 1,
- name: "What You Sell",
- },
- {
- id: 2,
- name: "Common Questions",
- },
- {
- id: 3,
- name: "Your Voice",
- },
-];
+import { SIDEKICK_SETUP_STEPS } from "@pilot/core/sidekick/personalization";
+
+export const sidekickSteps = [...SIDEKICK_SETUP_STEPS];
export const tone_options = ["Friendly", "Direct", "Like Me", "Custom"];
diff --git a/apps/app/src/lib/inngest/client.ts b/apps/app/src/lib/inngest/client.ts
index 423d205..f0454a7 100644
--- a/apps/app/src/lib/inngest/client.ts
+++ b/apps/app/src/lib/inngest/client.ts
@@ -1,4 +1,3 @@
import { Inngest } from "inngest";
-import { realtimeMiddleware } from "@inngest/realtime";
-export const inngest = new Inngest({ id: "pilot", middleware: [realtimeMiddleware()] });
\ No newline at end of file
+export const inngest = new Inngest({ id: "pilot" });
diff --git a/apps/app/src/lib/inngest/functions.ts b/apps/app/src/lib/inngest/functions.ts
index f418b42..10c26b3 100644
--- a/apps/app/src/lib/inngest/functions.ts
+++ b/apps/app/src/lib/inngest/functions.ts
@@ -23,8 +23,8 @@ export const syncInstagramContacts = inngest.createFunction(
{
id: "sync-instagram-contacts",
name: "Sync Instagram Contacts",
+ triggers: [{ event: "contacts/sync" }],
},
- { event: "contacts/sync" },
async ({ event, step }) => {
const { userId, fullSync = false } = event.data as {
userId?: string;
@@ -148,8 +148,11 @@ export const syncInstagramContacts = inngest.createFunction(
);
export const scheduleContactsSync = inngest.createFunction(
- { id: "schedule-contacts-sync", name: "Schedule Contacts Sync" },
- { cron: "0 * * * *" },
+ {
+ id: "schedule-contacts-sync",
+ name: "Schedule Contacts Sync",
+ triggers: [{ cron: "0 * * * *" }],
+ },
async ({ step }) => {
const integrations = await step.run("load-integrations", async () => {
return db.query.instagramIntegration.findMany({});
@@ -184,8 +187,8 @@ export const syncBusinessKnowledge = inngest.createFunction(
id: "sync-business-knowledge-memory",
name: "Sync Business Knowledge Memory",
retries: 1,
+ triggers: [{ event: "memory/knowledge.sync" }],
},
- { event: "memory/knowledge.sync" },
async ({ event, step }) => {
const { userId } = event.data as { userId?: string };
@@ -204,8 +207,8 @@ export const backfillActiveContactMemory = inngest.createFunction(
id: "backfill-active-contact-memory",
name: "Backfill Active Contact Memory",
retries: 0,
+ triggers: [{ event: "memory/contact.backfill" }],
},
- { event: "memory/contact.backfill" },
async ({ event, step }) => {
const { userId } = event.data as { userId?: string };
@@ -328,8 +331,11 @@ export const backfillActiveContactMemory = inngest.createFunction(
);
export const refreshInstagramTokens = inngest.createFunction(
- { id: "refresh-instagram-tokens", name: "Refresh Instagram Tokens" },
- { cron: "0 3 * * *" },
+ {
+ id: "refresh-instagram-tokens",
+ name: "Refresh Instagram Tokens",
+ triggers: [{ cron: "0 3 * * *" }],
+ },
async ({ step }) => {
const sevenDaysFromNow = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
diff --git a/apps/app/src/lib/inngest/retry-failed-send.ts b/apps/app/src/lib/inngest/retry-failed-send.ts
index b37c18c..a46e808 100644
--- a/apps/app/src/lib/inngest/retry-failed-send.ts
+++ b/apps/app/src/lib/inngest/retry-failed-send.ts
@@ -20,8 +20,8 @@ export const retryFailedInstagramSend = inngest.createFunction(
id: "retry-failed-instagram-send",
name: "Retry Failed Instagram Send",
retries: 0,
+ triggers: [{ event: "instagram/send-failed" }],
},
- { event: "instagram/send-failed" },
async ({ event, step }) => {
const {
igUserId,
diff --git a/packages/core/src/sidekick/personalization.ts b/packages/core/src/sidekick/personalization.ts
index 3c739d0..3e09ea1 100644
--- a/packages/core/src/sidekick/personalization.ts
+++ b/packages/core/src/sidekick/personalization.ts
@@ -9,6 +9,50 @@ import type { BusinessKnowledgeSnapshot } from "../memory/supermemory";
import type { Offer, UserPersonalizationData } from "@pilot/types/user";
import { eq } from "drizzle-orm";
+export const SIDEKICK_SETUP_STEPS = [
+ {
+ id: 0,
+ name: "Your Links",
+ },
+ {
+ id: 1,
+ name: "What You Sell",
+ },
+ {
+ id: 2,
+ name: "Common Questions",
+ },
+ {
+ id: 3,
+ name: "Your Voice",
+ },
+] as const;
+
+export type SidekickSetupStatus = {
+ sidekick_onboarding_complete: boolean;
+ isReady: boolean;
+ resumeStep: number;
+ resumeHref: string;
+ completedSteps: number;
+ totalSteps: number;
+ missing: string[];
+ steps: Array<{
+ id: number;
+ name: string;
+ complete: boolean;
+ }>;
+};
+
+export type SidekickSetupStatusResult =
+ | {
+ success: true;
+ data: SidekickSetupStatus;
+ }
+ | {
+ success: false;
+ error: string;
+ };
+
export const DEFAULT_SIDEKICK_PROMPT =
"You are Sidekick, Pilot's sales assistant. Reply as the business owner. Rely on retrieved memory and current context. Never invent business facts. Write like a real human: professional, natural, clear, direct, and conversational. Sound like you are explaining something to a smart friend over coffee. Use simple words and short sentences. Stay on point. Do not use buzzwords, corporate jargon, press-release language, fluff, or em dashes. Do not use the 'no x, no y, but z' pattern.";
@@ -30,6 +74,123 @@ const PROMPTS = {
},
} as const;
+export async function getSidekickSetupStatusByUserId(
+ dbClient: any,
+ userId: string,
+): Promise {
+ try {
+ const [userData, links, offers, toneProfiles, faqs] = await Promise.all([
+ dbClient
+ .select({
+ main_offering: user.main_offering,
+ sidekick_onboarding_complete: user.sidekick_onboarding_complete,
+ })
+ .from(user)
+ .where(eq(user.id, userId))
+ .then((rows: Array>) => rows[0]),
+ dbClient
+ .select()
+ .from(userOfferLink)
+ .where(eq(userOfferLink.userId, userId)),
+ dbClient.select().from(userOffer).where(eq(userOffer.userId, userId)),
+ dbClient
+ .select()
+ .from(userToneProfile)
+ .where(eq(userToneProfile.userId, userId))
+ .limit(1),
+ dbClient.select().from(userFaq).where(eq(userFaq.userId, userId)),
+ ]);
+
+ if (!userData) {
+ return { success: false, error: "User not found" } as const;
+ }
+
+ const hasPrimaryOfferLink = links.some(
+ (link: { type?: string; url?: string | null }) =>
+ link.type === "primary" && !!link.url?.trim(),
+ );
+ const hasOffer = offers.some(
+ (offer: { name?: string | null; content?: string | null }) =>
+ !!offer.name?.trim() && !!offer.content?.trim(),
+ );
+ const hasMainOffering =
+ typeof userData.main_offering === "string" &&
+ userData.main_offering.trim().length > 0;
+ const hasFaq = faqs.some(
+ (faq: { question?: string | null }) => !!faq.question?.trim(),
+ );
+ const hasToneProfile = !!toneProfiles[0]?.toneType;
+
+ const steps = SIDEKICK_SETUP_STEPS.map((step) => {
+ const complete =
+ step.id === 0
+ ? hasPrimaryOfferLink
+ : step.id === 1
+ ? hasOffer && hasMainOffering
+ : step.id === 2
+ ? hasFaq
+ : hasToneProfile;
+
+ return {
+ ...step,
+ complete,
+ };
+ });
+ const missing = [
+ !hasPrimaryOfferLink ? "main offer page" : null,
+ !hasOffer ? "at least one offer" : null,
+ !hasMainOffering ? "main offering" : null,
+ !hasFaq ? "at least one common question" : null,
+ !hasToneProfile ? "tone profile" : null,
+ ].filter((item): item is string => Boolean(item));
+ const firstIncompleteStep = steps.find((step) => !step.complete);
+ const hasRequiredData = missing.length === 0;
+ const resumeStep = firstIncompleteStep?.id ?? 0;
+ const persistedSidekickOnboardingComplete = Boolean(
+ userData.sidekick_onboarding_complete,
+ );
+
+ if (hasRequiredData && !persistedSidekickOnboardingComplete) {
+ try {
+ await dbClient
+ .update(user)
+ .set({ sidekick_onboarding_complete: true })
+ .where(eq(user.id, userId));
+ } catch (error) {
+ console.error(
+ "Failed to persist Sidekick onboarding completion:",
+ error,
+ );
+ }
+ }
+
+ const sidekick_onboarding_complete =
+ persistedSidekickOnboardingComplete || hasRequiredData;
+
+ return {
+ success: true,
+ data: {
+ sidekick_onboarding_complete,
+ isReady: hasRequiredData,
+ resumeStep,
+ resumeHref: hasRequiredData
+ ? "/"
+ : `/sidekick-onboarding?step=${resumeStep}`,
+ completedSteps: steps.filter((step) => step.complete).length,
+ totalSteps: steps.length,
+ missing,
+ steps,
+ },
+ } as const;
+ } catch (error) {
+ console.error("Error checking sidekick setup status:", error);
+ return {
+ success: false,
+ error: "Failed to check sidekick setup status",
+ } as const;
+ }
+}
+
export async function getPersonalizedSidekickDataByUserId(
dbClient: any,
userId: string,
@@ -240,7 +401,8 @@ function buildToneGuidanceFromProfile(
const samples = Array.isArray(toneProfileRecord?.sampleText)
? toneProfileRecord.sampleText.filter(Boolean).slice(0, 3)
: [];
- const sampleSuffix = samples.length > 0 ? ` Examples: ${samples.join(" | ")}` : "";
+ const sampleSuffix =
+ samples.length > 0 ? ` Examples: ${samples.join(" | ")}` : "";
switch (toneType) {
case "direct":
@@ -266,13 +428,20 @@ export async function getBusinessKnowledgeSnapshotByUserId(
return {
mainOffering: result.data.user?.main_offering || null,
- faqs: result.data.faqs.map((faq: { id: string; question: string; answer?: string | null }) => ({
- id: faq.id,
- question: faq.question,
- answer: faq.answer ?? null,
- })),
+ faqs: result.data.faqs.map(
+ (faq: { id: string; question: string; answer?: string | null }) => ({
+ id: faq.id,
+ question: faq.question,
+ answer: faq.answer ?? null,
+ }),
+ ),
offers: result.data.offers.map(
- (offer: { id: string; name: string; content: string; value?: number | null }) => ({
+ (offer: {
+ id: string;
+ name: string;
+ content: string;
+ value?: number | null;
+ }) => ({
id: offer.id,
name: offer.name,
content: offer.content,
diff --git a/packages/core/src/workflows/instagram-webhook.ts b/packages/core/src/workflows/instagram-webhook.ts
index 26e3410..cd7e5f5 100644
--- a/packages/core/src/workflows/instagram-webhook.ts
+++ b/packages/core/src/workflows/instagram-webhook.ts
@@ -1,4 +1,9 @@
-import { automation, contact, instagramIntegration, sidekickActionLog } from "@pilot/db/schema";
+import {
+ automation,
+ contact,
+ instagramIntegration,
+ sidekickActionLog,
+} from "@pilot/db/schema";
import { and, desc, eq, gt, or } from "drizzle-orm";
import {
postPublicCommentReply,
@@ -12,6 +17,7 @@ import { generateAutomationResponse } from "../automation/response";
import { classifyHumanResponseNeeded } from "../ai/hrn";
import { appendContactTranscriptMemory } from "../memory/supermemory";
import { generateReply } from "../sidekick/reply";
+import { getSidekickSetupStatusByUserId } from "../sidekick/personalization";
type GenericTemplateButton = {
type: string;
@@ -103,14 +109,17 @@ async function upsertContactState(params: {
});
}
-function isValidTemplateElement(value: unknown): value is GenericTemplateElement {
+function isValidTemplateElement(
+ value: unknown,
+): value is GenericTemplateElement {
if (!value || typeof value !== "object") {
return false;
}
const record = value as Record;
const titleOrText = record.title ?? record.text;
- const hasTitleOrText = typeof titleOrText === "string" && titleOrText.trim().length > 0;
+ const hasTitleOrText =
+ typeof titleOrText === "string" && titleOrText.trim().length > 0;
if (!hasTitleOrText) {
return false;
}
@@ -124,8 +133,12 @@ function normalizeTemplateElements(rawElements: GenericTemplateElement[]) {
typeof rawElement.title === "string" && rawElement.title
? rawElement.title
: (rawElement.text as string),
- subtitle: typeof rawElement.subtitle === "string" ? rawElement.subtitle : undefined,
- image_url: typeof rawElement.image_url === "string" ? rawElement.image_url : undefined,
+ subtitle:
+ typeof rawElement.subtitle === "string" ? rawElement.subtitle : undefined,
+ image_url:
+ typeof rawElement.image_url === "string"
+ ? rawElement.image_url
+ : undefined,
default_action:
rawElement.default_action &&
rawElement.default_action.type === "web_url" &&
@@ -135,7 +148,9 @@ function normalizeTemplateElements(rawElements: GenericTemplateElement[]) {
buttons: Array.isArray(rawElement.buttons)
? rawElement.buttons
.filter(
- (button): button is { type: "web_url"; url: string; title: string } =>
+ (
+ button,
+ ): button is { type: "web_url"; url: string; title: string } =>
!!button &&
typeof button === "object" &&
button.type === "web_url" &&
@@ -173,7 +188,8 @@ async function processCommentChanges(params: {
: typeof value.comment_id === "string"
? value.comment_id
: undefined;
- const commenterId = typeof value.from?.id === "string" ? value.from.id : undefined;
+ const commenterId =
+ typeof value.from?.id === "string" ? value.from.id : undefined;
const messageText = typeof value.text === "string" ? value.text : "";
const mediaId =
typeof value.media?.id === "string"
@@ -186,12 +202,13 @@ async function processCommentChanges(params: {
continue;
}
- const integration = await params.dbClient.query.instagramIntegration.findFirst({
- where: or(
- eq(instagramIntegration.instagramUserId, params.igUserId),
- eq(instagramIntegration.appScopedUserId, params.igUserId),
- ),
- });
+ const integration =
+ await params.dbClient.query.instagramIntegration.findFirst({
+ where: or(
+ eq(instagramIntegration.instagramUserId, params.igUserId),
+ eq(instagramIntegration.appScopedUserId, params.igUserId),
+ ),
+ });
if (!integration) {
continue;
}
@@ -221,7 +238,8 @@ async function processCommentChanges(params: {
prompt: matchedAutomation.responseContent,
userMessage: messageText,
});
- replyText = aiResponse?.text || "Thanks for your comment! We'll follow up in DMs.";
+ replyText =
+ aiResponse?.text || "Thanks for your comment! We'll follow up in DMs.";
}
let sendResponse:
@@ -231,7 +249,9 @@ async function processCommentChanges(params: {
if (matchedAutomation.responseType === "generic_template") {
try {
const parsed = JSON.parse(matchedAutomation.responseContent) as unknown;
- const elements = Array.isArray(parsed) ? parsed.filter(isValidTemplateElement) : [];
+ const elements = Array.isArray(parsed)
+ ? parsed.filter(isValidTemplateElement)
+ : [];
if (elements.length > 0) {
const normalized = normalizeTemplateElements(elements);
sendResponse = await sendInstagramCommentGenericTemplate({
@@ -270,7 +290,9 @@ async function processCommentChanges(params: {
messageId,
});
- const commentReplyText = (matchedAutomation as { commentReplyText?: string | null }).commentReplyText;
+ const commentReplyText = (
+ matchedAutomation as { commentReplyText?: string | null }
+ ).commentReplyText;
if (commentReplyText && commentReplyText.trim()) {
try {
await postPublicCommentReply({
@@ -294,12 +316,13 @@ async function processDirectMessage(params: {
webhookMid?: string | null;
resolveBillingStatus: (userId: string) => Promise;
}) {
- const integration = await params.dbClient.query.instagramIntegration.findFirst({
- where: or(
- eq(instagramIntegration.instagramUserId, params.igUserId),
- eq(instagramIntegration.appScopedUserId, params.igUserId),
- ),
- });
+ const integration =
+ await params.dbClient.query.instagramIntegration.findFirst({
+ where: or(
+ eq(instagramIntegration.instagramUserId, params.igUserId),
+ eq(instagramIntegration.appScopedUserId, params.igUserId),
+ ),
+ });
if (!integration) {
return { status: "ok" } as const;
@@ -367,7 +390,10 @@ async function processDirectMessage(params: {
}
const existingContact = await params.dbClient.query.contact.findFirst({
- where: and(eq(contact.userId, integration.userId), eq(contact.id, params.senderId)),
+ where: and(
+ eq(contact.userId, integration.userId),
+ eq(contact.id, params.senderId),
+ ),
});
if (!existingContact && !billingStatus.flags.canCreateContact) {
@@ -390,7 +416,9 @@ async function processDirectMessage(params: {
return { status: "ok", hrn: true } as const;
}
- const hrnDecision = await classifyHumanResponseNeeded({ message: params.messageText });
+ const hrnDecision = await classifyHumanResponseNeeded({
+ message: params.messageText,
+ });
if (hrnDecision.hrn) {
await upsertContactState({
dbClient: params.dbClient,
@@ -440,6 +468,31 @@ async function processDirectMessage(params: {
}
if (!replyText) {
+ const setupStatusResult = await getSidekickSetupStatusByUserId(
+ params.dbClient,
+ integration.userId,
+ );
+
+ if (!setupStatusResult.success) {
+ console.error(
+ "Failed to check Sidekick setup status",
+ setupStatusResult.error,
+ );
+ } else if (!setupStatusResult.data.isReady) {
+ await upsertContactState({
+ dbClient: params.dbClient,
+ contactId: params.senderId,
+ userId: integration.userId,
+ messageText: params.messageText,
+ stage: existingContact?.stage ?? "new",
+ sentiment: existingContact?.sentiment ?? "neutral",
+ leadScore: existingContact?.leadScore ?? 50,
+ requiresHRN: existingContact?.requiresHumanResponse ?? false,
+ humanResponseSetAt: existingContact?.humanResponseSetAt ?? null,
+ });
+ return { status: "ok", sidekickBlocked: true } as const;
+ }
+
const reply = await generateReply({
dbClient: params.dbClient,
userId: integration.userId,
@@ -480,8 +533,10 @@ async function processDirectMessage(params: {
const delivered = sendResponse.status >= 200 && sendResponse.status < 300;
const messageId =
- (sendResponse.data as { id?: string; message_id?: string } | undefined)?.id ||
- (sendResponse.data as { id?: string; message_id?: string } | undefined)?.message_id;
+ (sendResponse.data as { id?: string; message_id?: string } | undefined)
+ ?.id ||
+ (sendResponse.data as { id?: string; message_id?: string } | undefined)
+ ?.message_id;
const now = new Date();
await params.dbClient
@@ -623,7 +678,13 @@ export async function processInstagramWebhook(params: {
const messageText = message?.message?.text || "";
const isEcho = Boolean(message?.message?.is_echo);
- if (!entry?.id || !senderId || !messageText || isEcho || senderId === entry.id) {
+ if (
+ !entry?.id ||
+ !senderId ||
+ !messageText ||
+ isEcho ||
+ senderId === entry.id
+ ) {
return { status: "ok" } as const;
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 925377d..6528905 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -75,7 +75,7 @@ importers:
version: 0.34.17
'@sentry/nextjs':
specifier: ^10.39.0
- version: 10.39.0(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.101.2)
+ version: 10.39.0(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.101.2(esbuild@0.25.12))
'@t3-oss/env-nextjs':
specifier: ^0.13.10
version: 0.13.10(typescript@5.9.3)(zod@4.3.6)
@@ -93,10 +93,10 @@ importers:
version: 8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)
'@vercel/analytics':
specifier: ^1.6.1
- version: 1.6.1(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
+ version: 1.6.1(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
'@vercel/speed-insights':
specifier: ^1.3.1
- version: 1.3.1(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
+ version: 1.3.1(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
ai:
specifier: ^5.0.137
version: 5.0.137(zod@4.3.6)
@@ -125,8 +125,8 @@ importers:
specifier: ^2.0.6
version: 2.0.6
inngest:
- specifier: ^3.52.2
- version: 3.52.2(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(zod@4.3.6)
+ specifier: ^4.4.0
+ version: 4.4.0(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(zod@4.3.6)
lucide-react:
specifier: ^0.542.0
version: 0.542.0(react@19.2.4)
@@ -226,10 +226,10 @@ importers:
version: link:../../packages/ui
'@vercel/analytics':
specifier: ^1.5.0
- version: 1.6.1(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
+ version: 1.6.1(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
'@vercel/speed-insights':
specifier: ^1.2.0
- version: 1.3.1(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
+ version: 1.3.1(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
babel-plugin-react-compiler:
specifier: ^1.0.0
version: 1.0.0
@@ -275,7 +275,7 @@ importers:
version: 9.39.3(jiti@2.6.1)
eslint-config-next:
specifier: 16.1.6
- version: 16.1.6(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)
+ version: 16.1.6(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -5303,6 +5303,46 @@ packages:
typescript:
optional: true
+ inngest@4.4.0:
+ resolution: {integrity: sha512-OdNedhkjTK1EPij6k4nbQ70uWjxxjES1xJJPNFPp7XQJMyr0PrKg0SOoCL+FNsXp1Zrb7ZZG06sru9WHgFdE/g==}
+ engines: {node: '>=20'}
+ peerDependencies:
+ '@sveltejs/kit': '>=1.27.3'
+ '@vercel/node': '>=2.15.9'
+ aws-lambda: '>=1.0.7'
+ express: '>=4.19.2'
+ fastify: '>=4.21.0'
+ h3: '>=1.8.1'
+ hono: '>=4.2.7'
+ koa: '>=2.14.2'
+ next: '>=12.0.0'
+ react: '>=18.0.0'
+ typescript: '>=5.8.0'
+ zod: ^3.25.0 || ^4.0.0
+ peerDependenciesMeta:
+ '@sveltejs/kit':
+ optional: true
+ '@vercel/node':
+ optional: true
+ aws-lambda:
+ optional: true
+ express:
+ optional: true
+ fastify:
+ optional: true
+ h3:
+ optional: true
+ hono:
+ optional: true
+ koa:
+ optional: true
+ next:
+ optional: true
+ react:
+ optional: true
+ typescript:
+ optional: true
+
input-otp@1.4.2:
resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==}
peerDependencies:
@@ -9868,7 +9908,7 @@ snapshots:
'@sentry/core@10.39.0': {}
- '@sentry/nextjs@10.39.0(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.101.2)':
+ '@sentry/nextjs@10.39.0(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.101.2(esbuild@0.25.12))':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.39.0
@@ -9880,7 +9920,7 @@ snapshots:
'@sentry/opentelemetry': 10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)
'@sentry/react': 10.39.0(react@19.2.4)
'@sentry/vercel-edge': 10.39.0
- '@sentry/webpack-plugin': 4.9.1(webpack@5.101.2)
+ '@sentry/webpack-plugin': 4.9.1(webpack@5.101.2(esbuild@0.25.12))
next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
rollup: 4.58.0
stacktrace-parser: 0.1.11
@@ -9971,12 +10011,12 @@ snapshots:
'@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0)
'@sentry/core': 10.39.0
- '@sentry/webpack-plugin@4.9.1(webpack@5.101.2)':
+ '@sentry/webpack-plugin@4.9.1(webpack@5.101.2(esbuild@0.25.12))':
dependencies:
'@sentry/bundler-plugin-core': 4.9.1
unplugin: 1.0.1
uuid: 9.0.1
- webpack: 5.101.2
+ webpack: 5.101.2(esbuild@0.25.12)
transitivePeerDependencies:
- encoding
- supports-color
@@ -10172,11 +10212,11 @@ snapshots:
'@types/bunyan@1.8.11':
dependencies:
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
'@types/connect@3.4.38':
dependencies:
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
'@types/d3-array@3.2.2': {}
@@ -10337,13 +10377,13 @@ snapshots:
'@types/memcached@2.2.10':
dependencies:
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
'@types/ms@2.1.0': {}
'@types/mysql@2.15.27':
dependencies:
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
'@types/node@20.19.35':
dependencies:
@@ -10371,7 +10411,7 @@ snapshots:
'@types/oracledb@6.5.2':
dependencies:
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
'@types/pg-pool@2.0.7':
dependencies:
@@ -10379,7 +10419,7 @@ snapshots:
'@types/pg@8.15.6':
dependencies:
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
pg-protocol: 1.11.0
pg-types: 2.2.0
@@ -10403,7 +10443,7 @@ snapshots:
'@types/tedious@4.0.14':
dependencies:
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
'@types/triple-beam@1.3.5': {}
@@ -10649,14 +10689,14 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.11.1':
optional: true
- '@vercel/analytics@1.6.1(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)':
+ '@vercel/analytics@1.6.1(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)':
optionalDependencies:
next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
react: 19.2.4
'@vercel/oidc@3.1.0': {}
- '@vercel/speed-insights@1.3.1(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)':
+ '@vercel/speed-insights@1.3.1(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)':
optionalDependencies:
next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
react: 19.2.4
@@ -11762,8 +11802,28 @@ snapshots:
'@next/eslint-plugin-next': 16.1.6
eslint: 9.39.3(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1))
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1))
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.3(jiti@2.6.1))
+ eslint-plugin-react: 7.37.5(eslint@9.39.3(jiti@2.6.1))
+ eslint-plugin-react-hooks: 7.0.1(eslint@9.39.3(jiti@2.6.1))
+ globals: 16.4.0
+ typescript-eslint: 8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)
+ optionalDependencies:
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - '@typescript-eslint/parser'
+ - eslint-import-resolver-webpack
+ - eslint-plugin-import-x
+ - supports-color
+
+ eslint-config-next@16.1.6(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3):
+ dependencies:
+ '@next/eslint-plugin-next': 16.1.6
+ eslint: 9.39.3(jiti@2.6.1)
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.3(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.3(jiti@2.6.1))
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.3(jiti@2.6.1))
@@ -11785,7 +11845,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)):
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.6.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3
@@ -11796,22 +11856,22 @@ snapshots:
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.3(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)):
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -11822,7 +11882,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.39.3(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1))
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -11840,6 +11900,33 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
+ eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1)):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.9
+ array.prototype.findlastindex: 1.2.6
+ array.prototype.flat: 1.3.3
+ array.prototype.flatmap: 1.3.3
+ debug: 3.2.7
+ doctrine: 2.1.0
+ eslint: 9.39.3(jiti@2.6.1)
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1))
+ hasown: 2.0.2
+ is-core-module: 2.16.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.1
+ semver: 6.3.1
+ string.prototype.trimend: 1.0.9
+ tsconfig-paths: 3.15.0
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.3(jiti@2.6.1)):
dependencies:
aria-query: 5.3.2
@@ -12464,7 +12551,7 @@ snapshots:
- encoding
- supports-color
- inngest@3.52.2(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(zod@4.3.6):
+ inngest@4.4.0(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(zod@4.3.6):
dependencies:
'@bufbuild/protobuf': 2.11.0
'@inngest/ai': 0.1.7
@@ -12481,19 +12568,18 @@ snapshots:
'@types/debug': 4.1.12
'@types/ms': 2.1.0
canonicalize: 1.0.8
- chalk: 4.1.2
cross-fetch: 4.1.0
debug: 4.4.3
hash.js: 1.1.7
json-stringify-safe: 5.0.1
ms: 2.1.3
serialize-error-cjs: 0.1.4
- strip-ansi: 5.2.0
temporal-polyfill: 0.2.5
ulid: 2.4.0
zod: 4.3.6
optionalDependencies:
next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+ react: 19.2.4
typescript: 5.9.3
transitivePeerDependencies:
- '@opentelemetry/core'
@@ -13679,7 +13765,7 @@ snapshots:
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
long: 5.3.2
protobufjs@8.0.0:
@@ -13694,7 +13780,7 @@ snapshots:
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
- '@types/node': 24.12.2
+ '@types/node': 25.6.0
long: 5.3.2
proxy-from-env@1.1.0: {}
@@ -14426,13 +14512,15 @@ snapshots:
temporal-spec@0.2.4: {}
- terser-webpack-plugin@5.4.0(webpack@5.101.2):
+ terser-webpack-plugin@5.4.0(esbuild@0.25.12)(webpack@5.101.2(esbuild@0.25.12)):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
schema-utils: 4.3.3
terser: 5.46.1
- webpack: 5.101.2
+ webpack: 5.101.2(esbuild@0.25.12)
+ optionalDependencies:
+ esbuild: 0.25.12
terser@5.46.1:
dependencies:
@@ -14784,7 +14872,7 @@ snapshots:
webpack-virtual-modules@0.5.0: {}
- webpack@5.101.2:
+ webpack@5.101.2(esbuild@0.25.12):
dependencies:
'@types/eslint-scope': 3.7.7
'@types/estree': 1.0.8
@@ -14808,7 +14896,7 @@ snapshots:
neo-async: 2.6.2
schema-utils: 4.3.3
tapable: 2.3.2
- terser-webpack-plugin: 5.4.0(webpack@5.101.2)
+ terser-webpack-plugin: 5.4.0(esbuild@0.25.12)(webpack@5.101.2(esbuild@0.25.12))
watchpack: 2.5.1
webpack-sources: 3.3.4
transitivePeerDependencies: