diff --git a/README.md b/README.md
index 554db57..475c162 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
## TypeScript Email Assistant Implementation
-The TypeScript Email Assistant (`app/page.tsx`) implements an LLM-powered workflow to process emails:
+This is a vanilla TypeScript project for email assistant workflows using LangChain and LangGraph.
### Architecture
- Uses `StateGraph` from LangGraph to create a multi-step workflow
@@ -43,17 +43,37 @@ The TypeScript Email Assistant (`app/page.tsx`) implements an LLM-powered workfl
- Properly typed with TypeScript for complete type safety
- Uses command pattern to transition between states
-### P2
-- [ ] notebooks
-- [ ] video course [1,2,3,4]
+### Project Structure
+- `scripts/`: TypeScript scripts to run the email assistant
+- `lib/`: Utility functions, tools, and shared types
+ - `lib/tools/`: Tool implementations
+ - `lib/prompts.ts`: Prompt templates
+ - `lib/schemas.ts`: TypeScript/Zod schemas
+ - `lib/utils.ts`: Utility functions
-### New TS files
-- `lib/tools/`
-- `lib/prompts.ts`
-- `lib/schemas.ts`
-- `lib/utils.ts`
+## Getting Started
-### Course outline
+First, install dependencies:
+
+```bash
+npm install
+# or
+yarn
+# or
+pnpm install
+```
+
+Then, run the development script:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+```
+
+## Course outline
> BUILD
> EVAL
diff --git a/app/api/email-assistant/route.ts b/app/api/email-assistant/route.ts
deleted file mode 100644
index 7047478..0000000
--- a/app/api/email-assistant/route.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { NextRequest, NextResponse } from "next/server";
-import { getEmailAssistant } from "../../page";
-import { EmailData } from "../../../lib/schemas";
-
-// This is a temporary API route for testing the email assistant (before setting up LangSmith)
-export async function POST(request: NextRequest) {
- try {
- const data = await request.json();
- const { email_input } = data;
-
- // Get the email assistant
- const emailAssistant = await getEmailAssistant();
-
- // Run the email assistant with the raw email text
- const result = await emailAssistant.invoke({
- email_input
- });
-
- return NextResponse.json(result);
- } catch (error: any) {
- console.error("Error running email assistant:", error);
- return NextResponse.json(
- { error: error.message },
- { status: 500 }
- );
- }
-}
\ No newline at end of file
diff --git a/app/favicon.ico b/app/favicon.ico
deleted file mode 100644
index 718d6fe..0000000
Binary files a/app/favicon.ico and /dev/null differ
diff --git a/app/globals.css b/app/globals.css
deleted file mode 100644
index dc98be7..0000000
--- a/app/globals.css
+++ /dev/null
@@ -1,122 +0,0 @@
-@import "tailwindcss";
-@import "tw-animate-css";
-
-@custom-variant dark (&:is(.dark *));
-
-@theme inline {
- --color-background: var(--background);
- --color-foreground: var(--foreground);
- --font-sans: var(--font-geist-sans);
- --font-mono: var(--font-geist-mono);
- --color-sidebar-ring: var(--sidebar-ring);
- --color-sidebar-border: var(--sidebar-border);
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
- --color-sidebar-accent: var(--sidebar-accent);
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
- --color-sidebar-primary: var(--sidebar-primary);
- --color-sidebar-foreground: var(--sidebar-foreground);
- --color-sidebar: var(--sidebar);
- --color-chart-5: var(--chart-5);
- --color-chart-4: var(--chart-4);
- --color-chart-3: var(--chart-3);
- --color-chart-2: var(--chart-2);
- --color-chart-1: var(--chart-1);
- --color-ring: var(--ring);
- --color-input: var(--input);
- --color-border: var(--border);
- --color-destructive: var(--destructive);
- --color-accent-foreground: var(--accent-foreground);
- --color-accent: var(--accent);
- --color-muted-foreground: var(--muted-foreground);
- --color-muted: var(--muted);
- --color-secondary-foreground: var(--secondary-foreground);
- --color-secondary: var(--secondary);
- --color-primary-foreground: var(--primary-foreground);
- --color-primary: var(--primary);
- --color-popover-foreground: var(--popover-foreground);
- --color-popover: var(--popover);
- --color-card-foreground: var(--card-foreground);
- --color-card: var(--card);
- --radius-sm: calc(var(--radius) - 4px);
- --radius-md: calc(var(--radius) - 2px);
- --radius-lg: var(--radius);
- --radius-xl: calc(var(--radius) + 4px);
-}
-
-:root {
- --radius: 0.625rem;
- --background: oklch(1 0 0);
- --foreground: oklch(0.145 0 0);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.145 0 0);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.145 0 0);
- --primary: oklch(0.205 0 0);
- --primary-foreground: oklch(0.985 0 0);
- --secondary: oklch(0.97 0 0);
- --secondary-foreground: oklch(0.205 0 0);
- --muted: oklch(0.97 0 0);
- --muted-foreground: oklch(0.556 0 0);
- --accent: oklch(0.97 0 0);
- --accent-foreground: oklch(0.205 0 0);
- --destructive: oklch(0.577 0.245 27.325);
- --border: oklch(0.922 0 0);
- --input: oklch(0.922 0 0);
- --ring: oklch(0.708 0 0);
- --chart-1: oklch(0.646 0.222 41.116);
- --chart-2: oklch(0.6 0.118 184.704);
- --chart-3: oklch(0.398 0.07 227.392);
- --chart-4: oklch(0.828 0.189 84.429);
- --chart-5: oklch(0.769 0.188 70.08);
- --sidebar: oklch(0.985 0 0);
- --sidebar-foreground: oklch(0.145 0 0);
- --sidebar-primary: oklch(0.205 0 0);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.97 0 0);
- --sidebar-accent-foreground: oklch(0.205 0 0);
- --sidebar-border: oklch(0.922 0 0);
- --sidebar-ring: oklch(0.708 0 0);
-}
-
-.dark {
- --background: oklch(0.145 0 0);
- --foreground: oklch(0.985 0 0);
- --card: oklch(0.205 0 0);
- --card-foreground: oklch(0.985 0 0);
- --popover: oklch(0.205 0 0);
- --popover-foreground: oklch(0.985 0 0);
- --primary: oklch(0.922 0 0);
- --primary-foreground: oklch(0.205 0 0);
- --secondary: oklch(0.269 0 0);
- --secondary-foreground: oklch(0.985 0 0);
- --muted: oklch(0.269 0 0);
- --muted-foreground: oklch(0.708 0 0);
- --accent: oklch(0.269 0 0);
- --accent-foreground: oklch(0.985 0 0);
- --destructive: oklch(0.704 0.191 22.216);
- --border: oklch(1 0 0 / 10%);
- --input: oklch(1 0 0 / 15%);
- --ring: oklch(0.556 0 0);
- --chart-1: oklch(0.488 0.243 264.376);
- --chart-2: oklch(0.696 0.17 162.48);
- --chart-3: oklch(0.769 0.188 70.08);
- --chart-4: oklch(0.627 0.265 303.9);
- --chart-5: oklch(0.645 0.246 16.439);
- --sidebar: oklch(0.205 0 0);
- --sidebar-foreground: oklch(0.985 0 0);
- --sidebar-primary: oklch(0.488 0.243 264.376);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.269 0 0);
- --sidebar-accent-foreground: oklch(0.985 0 0);
- --sidebar-border: oklch(1 0 0 / 10%);
- --sidebar-ring: oklch(0.556 0 0);
-}
-
-@layer base {
- * {
- @apply border-border outline-ring/50;
- }
- body {
- @apply bg-background text-foreground;
- }
-}
diff --git a/app/layout.tsx b/app/layout.tsx
deleted file mode 100644
index f7fa87e..0000000
--- a/app/layout.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
-import "./globals.css";
-
-const geistSans = Geist({
- variable: "--font-geist-sans",
- subsets: ["latin"],
-});
-
-const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
- subsets: ["latin"],
-});
-
-export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
-
-
- {children}
-
-
- );
-}
diff --git a/app/test-email-assistant/page.tsx b/app/test-email-assistant/page.tsx
deleted file mode 100644
index b398167..0000000
--- a/app/test-email-assistant/page.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-"use client";
-// this is a temporary page for testing the email assistant (before setting up LangSmith)
-import { useState } from "react";
-import { EmailData } from "../../lib/schemas";
-import { BaseMessage } from "@langchain/core/messages";
-
-export default function TestEmailAssistant() {
- const [emailInput, setEmailInput] = useState(
- `From: john@example.com
-To: assistant@company.com
-Subject: Meeting Request
-
-Hi there,
-
-Can we schedule a meeting for tomorrow at 2pm to discuss the project?
-
-Thanks,
-John`
- );
-
- const [output, setOutput] = useState({});
- const [loading, setLoading] = useState(false);
- const [messages, setMessages] = useState([]);
-
- async function runTest() {
- setLoading(true);
- try {
- // Call the API route instead of directly invoking the assistant
- const response = await fetch('/api/email-assistant', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- email_input: emailInput
- }),
- });
-
- if (!response.ok) {
- throw new Error(`API responded with status: ${response.status}`);
- }
-
- const result = await response.json();
-
- setOutput(result);
-
- // Extract messages if they exist
- if (result.messages && Array.isArray(result.messages)) {
- setMessages(result.messages);
- }
- } catch (error: any) {
- console.error("Error running email assistant:", error);
- setOutput({ error: error.message });
- } finally {
- setLoading(false);
- }
- }
-
- return (
-
-
Email Assistant Test
-
-
-
-
-
-
-
-
-
Classification Result
-
- {output.classification_decision && (
-
- {output.classification_decision === "respond" && "📧 RESPOND - This email requires a response"}
- {output.classification_decision === "ignore" && "🚫 IGNORE - This email can be safely ignored"}
- {output.classification_decision === "notify" && "🔔 NOTIFY - This email contains important information"}
-
- )}
-
-
-
- {messages.length > 0 && (
-
-
Messages
-
- {messages.map((message, index) => (
-
-
{message.constructor.name}
-
{String(message.content)}
- {message.additional_kwargs?.tool_calls && (
-
-
Tool Calls:
-
- {JSON.stringify(message.additional_kwargs.tool_calls, null, 2)}
-
-
- )}
-
- ))}
-
-
- )}
-
-
-
Full Output
-
- {JSON.stringify(output, null, 2)}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/components.json b/components.json
deleted file mode 100644
index 335484f..0000000
--- a/components.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "$schema": "https://ui.shadcn.com/schema.json",
- "style": "new-york",
- "rsc": true,
- "tsx": true,
- "tailwind": {
- "config": "",
- "css": "app/globals.css",
- "baseColor": "neutral",
- "cssVariables": true,
- "prefix": ""
- },
- "aliases": {
- "components": "@/components",
- "utils": "@/lib/utils",
- "ui": "@/components/ui",
- "lib": "@/lib",
- "hooks": "@/hooks"
- },
- "iconLibrary": "lucide"
-}
\ No newline at end of file
diff --git a/eslint.config.mjs b/eslint.config.mjs
index c85fb67..200ed3e 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -10,7 +10,7 @@ const compat = new FlatCompat({
});
const eslintConfig = [
- ...compat.extends("next/core-web-vitals", "next/typescript"),
+ ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"),
];
export default eslintConfig;
diff --git a/next.config.ts b/next.config.ts
deleted file mode 100644
index e9ffa30..0000000
--- a/next.config.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { NextConfig } from "next";
-
-const nextConfig: NextConfig = {
- /* config options here */
-};
-
-export default nextConfig;
diff --git a/package.json b/package.json
index 3126358..087efd2 100644
--- a/package.json
+++ b/package.json
@@ -2,38 +2,25 @@
"name": "email-assistant",
"version": "0.1.0",
"private": true,
+ "type": "module",
"scripts": {
- "dev": "next dev --turbopack",
- "build": "next build",
- "start": "next start",
- "lint": "next lint",
+ "dev": "tsx scripts/dev.ts",
+ "lint": "eslint .",
"agent": "langgraphjs dev"
},
"dependencies": {
"@langchain/core": "^0.3.49",
"@langchain/langgraph": "^0.2.67",
- "class-variance-authority": "^0.7.1",
- "clsx": "^2.1.1",
"langchain": "^0.3.24",
- "lucide-react": "^0.503.0",
- "next": "15.3.1",
- "react": "^19.0.0",
- "react-dom": "^19.0.0",
- "tailwind-merge": "^3.2.0",
"zod": "^3.24.3"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@langchain/langgraph-cli": "^0.0.30",
- "@tailwindcss/postcss": "^4",
"@types/node": "^20",
- "@types/react": "^19",
- "@types/react-dom": "^19",
"eslint": "^9",
- "eslint-config-next": "15.3.1",
- "tailwindcss": "^4",
- "tw-animate-css": "^1.2.8",
- "typescript": "^5"
+ "typescript": "^5",
+ "tsx": "^4.7.0"
},
"packageManager": "pnpm@10.9.0+sha512.0486e394640d3c1fb3c9d43d49cf92879ff74f8516959c235308f5a8f62e2e19528a65cdc2a3058f587cde71eba3d5b56327c8c33a97e4c4051ca48a10ca2d5f"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6ed2253..7797725 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10,34 +10,13 @@ importers:
dependencies:
'@langchain/core':
specifier: ^0.3.49
- version: 0.3.49(openai@4.96.2(zod@3.24.3))
+ version: 0.3.50(openai@4.96.2(zod@3.24.3))
'@langchain/langgraph':
specifier: ^0.2.67
- version: 0.2.67(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)(zod-to-json-schema@3.24.5(zod@3.24.3))
- class-variance-authority:
- specifier: ^0.7.1
- version: 0.7.1
- clsx:
- specifier: ^2.1.1
- version: 2.1.1
+ version: 0.2.67(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(zod-to-json-schema@3.24.5(zod@3.24.3))
langchain:
specifier: ^0.3.24
- version: 0.3.24(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))
- lucide-react:
- specifier: ^0.503.0
- version: 0.503.0(react@19.1.0)
- next:
- specifier: 15.3.1
- version: 15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- react:
- specifier: ^19.0.0
- version: 19.1.0
- react-dom:
- specifier: ^19.0.0
- version: 19.1.0(react@19.1.0)
- tailwind-merge:
- specifier: ^3.2.0
- version: 3.2.0
+ version: 0.3.24(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))
zod:
specifier: ^3.24.3
version: 3.24.3
@@ -47,31 +26,16 @@ importers:
version: 3.3.1
'@langchain/langgraph-cli':
specifier: ^0.0.30
- version: 0.0.30(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3))))(@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0))(@langchain/langgraph@0.2.67(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)(zod-to-json-schema@3.24.5(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))(typescript@5.8.3)
- '@tailwindcss/postcss':
- specifier: ^4
- version: 4.1.4
+ version: 0.0.30(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3))))(@langchain/langgraph@0.2.67(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(zod-to-json-schema@3.24.5(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))(typescript@5.8.3)
'@types/node':
specifier: ^20
version: 20.17.32
- '@types/react':
- specifier: ^19
- version: 19.1.2
- '@types/react-dom':
- specifier: ^19
- version: 19.1.2(@types/react@19.1.2)
eslint:
specifier: ^9
version: 9.25.1(jiti@2.4.2)
- eslint-config-next:
- specifier: 15.3.1
- version: 15.3.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- tailwindcss:
- specifier: ^4
- version: 4.1.4
- tw-animate-css:
- specifier: ^1.2.8
- version: 1.2.8
+ tsx:
+ specifier: ^4.7.0
+ version: 4.19.4
typescript:
specifier: ^5
version: 5.8.3
@@ -105,15 +69,6 @@ packages:
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
- '@emnapi/core@1.4.3':
- resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==}
-
- '@emnapi/runtime@1.4.3':
- resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
-
- '@emnapi/wasi-threads@1.0.2':
- resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==}
-
'@esbuild/aix-ppc64@0.25.3':
resolution: {integrity: sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==}
engines: {node: '>=18'}
@@ -278,8 +233,8 @@ packages:
resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/config-helpers@0.2.1':
- resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==}
+ '@eslint/config-helpers@0.2.2':
+ resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/core@0.13.0':
@@ -334,122 +289,12 @@ packages:
resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
engines: {node: '>=18.18'}
- '@img/sharp-darwin-arm64@0.34.1':
- resolution: {integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [arm64]
- os: [darwin]
-
- '@img/sharp-darwin-x64@0.34.1':
- resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [x64]
- os: [darwin]
-
- '@img/sharp-libvips-darwin-arm64@1.1.0':
- resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==}
- cpu: [arm64]
- os: [darwin]
-
- '@img/sharp-libvips-darwin-x64@1.1.0':
- resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==}
- cpu: [x64]
- os: [darwin]
-
- '@img/sharp-libvips-linux-arm64@1.1.0':
- resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==}
- cpu: [arm64]
- os: [linux]
-
- '@img/sharp-libvips-linux-arm@1.1.0':
- resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==}
- cpu: [arm]
- os: [linux]
-
- '@img/sharp-libvips-linux-ppc64@1.1.0':
- resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==}
- cpu: [ppc64]
- os: [linux]
-
- '@img/sharp-libvips-linux-s390x@1.1.0':
- resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==}
- cpu: [s390x]
- os: [linux]
-
- '@img/sharp-libvips-linux-x64@1.1.0':
- resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==}
- cpu: [x64]
- os: [linux]
-
- '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
- resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==}
- cpu: [arm64]
- os: [linux]
-
- '@img/sharp-libvips-linuxmusl-x64@1.1.0':
- resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==}
- cpu: [x64]
- os: [linux]
-
- '@img/sharp-linux-arm64@0.34.1':
- resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [arm64]
- os: [linux]
-
- '@img/sharp-linux-arm@0.34.1':
- resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [arm]
- os: [linux]
-
- '@img/sharp-linux-s390x@0.34.1':
- resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [s390x]
- os: [linux]
-
- '@img/sharp-linux-x64@0.34.1':
- resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [x64]
- os: [linux]
-
- '@img/sharp-linuxmusl-arm64@0.34.1':
- resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [arm64]
- os: [linux]
-
- '@img/sharp-linuxmusl-x64@0.34.1':
- resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [x64]
- os: [linux]
-
- '@img/sharp-wasm32@0.34.1':
- resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [wasm32]
-
- '@img/sharp-win32-ia32@0.34.1':
- resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [ia32]
- os: [win32]
-
- '@img/sharp-win32-x64@0.34.1':
- resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- cpu: [x64]
- os: [win32]
-
'@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
engines: {node: '>=18.0.0'}
- '@langchain/core@0.3.49':
- resolution: {integrity: sha512-ESEbxjtVtZTpgMPrLFEQZVHA5EVJuDYV3XzTFQ8haPHmBJfzskl6TINFttoGKTGbv799DZrRwnd5fbuCN5NDEA==}
+ '@langchain/core@0.3.50':
+ resolution: {integrity: sha512-0MBVe7dZ4h3a3MJAg/YesWBvwkDg8t2rDIGg2Q11DxRBnxB7OqmvBlbZ1ftaDvoBZzxMY+8E58OsCLuay3Bk9w==}
engines: {node: '>=18'}
'@langchain/langgraph-api@0.0.30':
@@ -476,8 +321,8 @@ packages:
engines: {node: ^18.19.0 || >=20.16.0}
hasBin: true
- '@langchain/langgraph-sdk@0.0.70':
- resolution: {integrity: sha512-O8I12bfeMVz5fOrXnIcK4IdRf50IqyJTO458V56wAIHLNoi4H8/JHM+2M+Y4H2PtslXIGnvomWqlBd0eY5z/Og==}
+ '@langchain/langgraph-sdk@0.0.74':
+ resolution: {integrity: sha512-IUN0m4BYkGWdviFd4EaWDcQgxNq8z+1LIwXajCSt9B+Cb/pz0ZNpIPdu5hAIsf6a0RWu5yRUhzL1L40t7vu3Zg==}
peerDependencies:
'@langchain/core': '>=0.2.31 <0.4.0'
react: ^18 || ^19
@@ -502,8 +347,8 @@ packages:
zod-to-json-schema:
optional: true
- '@langchain/openai@0.5.7':
- resolution: {integrity: sha512-2azobl6XqPJIwe1MC4G33/77sOuvWtsbv2Wa8K6qsEEmIsznOXBXrOcKrp+1PTxRKy3UkIV2JV9mQas+ewPU0Q==}
+ '@langchain/openai@0.5.8':
+ resolution: {integrity: sha512-ppHsM3CiaGFglwwC9iFZ6XdRcr2kiX9WMXJWAxFbcyeB32n2tyoYOLZj7fSBD+6ASu/VuM1uhwVbgg25RrULpA==}
engines: {node: '>=18'}
peerDependencies:
'@langchain/core': '>=0.3.48 <0.4.0'
@@ -514,85 +359,6 @@ packages:
peerDependencies:
'@langchain/core': '>=0.2.21 <0.4.0'
- '@napi-rs/wasm-runtime@0.2.9':
- resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==}
-
- '@next/env@15.3.1':
- resolution: {integrity: sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==}
-
- '@next/eslint-plugin-next@15.3.1':
- resolution: {integrity: sha512-oEs4dsfM6iyER3jTzMm4kDSbrQJq8wZw5fmT6fg2V3SMo+kgG+cShzLfEV20senZzv8VF+puNLheiGPlBGsv2A==}
-
- '@next/swc-darwin-arm64@15.3.1':
- resolution: {integrity: sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w==}
- engines: {node: '>= 10'}
- cpu: [arm64]
- os: [darwin]
-
- '@next/swc-darwin-x64@15.3.1':
- resolution: {integrity: sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g==}
- engines: {node: '>= 10'}
- cpu: [x64]
- os: [darwin]
-
- '@next/swc-linux-arm64-gnu@15.3.1':
- resolution: {integrity: sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ==}
- engines: {node: '>= 10'}
- cpu: [arm64]
- os: [linux]
-
- '@next/swc-linux-arm64-musl@15.3.1':
- resolution: {integrity: sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg==}
- engines: {node: '>= 10'}
- cpu: [arm64]
- os: [linux]
-
- '@next/swc-linux-x64-gnu@15.3.1':
- resolution: {integrity: sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A==}
- engines: {node: '>= 10'}
- cpu: [x64]
- os: [linux]
-
- '@next/swc-linux-x64-musl@15.3.1':
- resolution: {integrity: sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA==}
- engines: {node: '>= 10'}
- cpu: [x64]
- os: [linux]
-
- '@next/swc-win32-arm64-msvc@15.3.1':
- resolution: {integrity: sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw==}
- engines: {node: '>= 10'}
- cpu: [arm64]
- os: [win32]
-
- '@next/swc-win32-x64-msvc@15.3.1':
- resolution: {integrity: sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ==}
- engines: {node: '>= 10'}
- cpu: [x64]
- os: [win32]
-
- '@nodelib/fs.scandir@2.1.5':
- resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
- engines: {node: '>= 8'}
-
- '@nodelib/fs.stat@2.0.5':
- resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
- engines: {node: '>= 8'}
-
- '@nodelib/fs.walk@1.2.8':
- resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
- engines: {node: '>= 8'}
-
- '@nolyfill/is-core-module@1.0.39':
- resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
- engines: {node: '>=12.4.0'}
-
- '@rtsao/scc@1.1.0':
- resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
-
- '@rushstack/eslint-patch@1.11.0':
- resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==}
-
'@sec-ant/readable-stream@0.4.1':
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
@@ -600,71 +366,65 @@ packages:
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
engines: {node: '>=18'}
- '@swc/counter@0.1.3':
- resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+ '@tailwindcss/node@4.1.5':
+ resolution: {integrity: sha512-CBhSWo0vLnWhXIvpD0qsPephiaUYfHUX3U9anwDaHZAeuGpTiB3XmsxPAN6qX7bFhipyGBqOa1QYQVVhkOUGxg==}
- '@swc/helpers@0.5.15':
- resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
-
- '@tailwindcss/node@4.1.4':
- resolution: {integrity: sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==}
-
- '@tailwindcss/oxide-android-arm64@4.1.4':
- resolution: {integrity: sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==}
+ '@tailwindcss/oxide-android-arm64@4.1.5':
+ resolution: {integrity: sha512-LVvM0GirXHED02j7hSECm8l9GGJ1RfgpWCW+DRn5TvSaxVsv28gRtoL4aWKGnXqwvI3zu1GABeDNDVZeDPOQrw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
- '@tailwindcss/oxide-darwin-arm64@4.1.4':
- resolution: {integrity: sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==}
+ '@tailwindcss/oxide-darwin-arm64@4.1.5':
+ resolution: {integrity: sha512-//TfCA3pNrgnw4rRJOqavW7XUk8gsg9ddi8cwcsWXp99tzdBAZW0WXrD8wDyNbqjW316Pk2hiN/NJx/KWHl8oA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@tailwindcss/oxide-darwin-x64@4.1.4':
- resolution: {integrity: sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==}
+ '@tailwindcss/oxide-darwin-x64@4.1.5':
+ resolution: {integrity: sha512-XQorp3Q6/WzRd9OalgHgaqgEbjP3qjHrlSUb5k1EuS1Z9NE9+BbzSORraO+ecW432cbCN7RVGGL/lSnHxcd+7Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@tailwindcss/oxide-freebsd-x64@4.1.4':
- resolution: {integrity: sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==}
+ '@tailwindcss/oxide-freebsd-x64@4.1.5':
+ resolution: {integrity: sha512-bPrLWbxo8gAo97ZmrCbOdtlz/Dkuy8NK97aFbVpkJ2nJ2Jo/rsCbu0TlGx8joCuA3q6vMWTSn01JY46iwG+clg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.4':
- resolution: {integrity: sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==}
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5':
+ resolution: {integrity: sha512-1gtQJY9JzMAhgAfvd/ZaVOjh/Ju/nCoAsvOVJenWZfs05wb8zq+GOTnZALWGqKIYEtyNpCzvMk+ocGpxwdvaVg==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.4':
- resolution: {integrity: sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==}
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.5':
+ resolution: {integrity: sha512-dtlaHU2v7MtdxBXoqhxwsWjav7oim7Whc6S9wq/i/uUMTWAzq/gijq1InSgn2yTnh43kR+SFvcSyEF0GCNu1PQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-musl@4.1.4':
- resolution: {integrity: sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==}
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.5':
+ resolution: {integrity: sha512-fg0F6nAeYcJ3CriqDT1iVrqALMwD37+sLzXs8Rjy8Z1ZHshJoYceodfyUwGJEsQoTyWbliFNRs2wMQNXtT7MVA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-gnu@4.1.4':
- resolution: {integrity: sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==}
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.5':
+ resolution: {integrity: sha512-SO+F2YEIAHa1AITwc8oPwMOWhgorPzzcbhWEb+4oLi953h45FklDmM8dPSZ7hNHpIk9p/SCZKUYn35t5fjGtHA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-musl@4.1.4':
- resolution: {integrity: sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==}
+ '@tailwindcss/oxide-linux-x64-musl@4.1.5':
+ resolution: {integrity: sha512-6UbBBplywkk/R+PqqioskUeXfKcBht3KU7juTi1UszJLx0KPXUo10v2Ok04iBJIaDPkIFkUOVboXms5Yxvaz+g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-wasm32-wasi@4.1.4':
- resolution: {integrity: sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==}
+ '@tailwindcss/oxide-wasm32-wasi@4.1.5':
+ resolution: {integrity: sha512-hwALf2K9FHuiXTPqmo1KeOb83fTRNbe9r/Ixv9ZNQ/R24yw8Ge1HOWDDgTdtzntIaIUJG5dfXCf4g9AD4RiyhQ==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
@@ -675,27 +435,24 @@ packages:
- '@emnapi/wasi-threads'
- tslib
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.4':
- resolution: {integrity: sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==}
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.5':
+ resolution: {integrity: sha512-oDKncffWzaovJbkuR7/OTNFRJQVdiw/n8HnzaCItrNQUeQgjy7oUiYpsm9HUBgpmvmDpSSbGaCa2Evzvk3eFmA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
- '@tailwindcss/oxide-win32-x64-msvc@4.1.4':
- resolution: {integrity: sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==}
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.5':
+ resolution: {integrity: sha512-WiR4dtyrFdbb+ov0LK+7XsFOsG+0xs0PKZKkt41KDn9jYpO7baE3bXiudPVkTqUEwNfiglCygQHl2jklvSBi7Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@tailwindcss/oxide@4.1.4':
- resolution: {integrity: sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==}
+ '@tailwindcss/oxide@4.1.5':
+ resolution: {integrity: sha512-1n4br1znquEvyW/QuqMKQZlBen+jxAbvyduU87RS8R3tUSvByAkcaMTkJepNIrTlYhD+U25K4iiCIxE6BGdRYA==}
engines: {node: '>= 10'}
- '@tailwindcss/postcss@4.1.4':
- resolution: {integrity: sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw==}
-
- '@tybys/wasm-util@0.9.0':
- resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
+ '@tailwindcss/postcss@4.1.5':
+ resolution: {integrity: sha512-5lAC2/pzuyfhsFgk6I58HcNy6vPK3dV/PoPxSDuOTVbDvCddYHzHiJZZInGIY0venvzzfrTEUAXJFULAfFmObg==}
'@types/estree@1.0.7':
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
@@ -703,9 +460,6 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
- '@types/json5@0.0.29':
- resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
-
'@types/node-fetch@2.6.12':
resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==}
@@ -715,14 +469,6 @@ packages:
'@types/node@20.17.32':
resolution: {integrity: sha512-zeMXFn8zQ+UkjK4ws0RiOC9EWByyW1CcVmLe+2rQocXRsGEDxUCwPEIVgpsGcLHS/P8JkT0oa3839BRABS0oPw==}
- '@types/react-dom@19.1.2':
- resolution: {integrity: sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==}
- peerDependencies:
- '@types/react': ^19.0.0
-
- '@types/react@19.1.2':
- resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==}
-
'@types/retry@0.12.0':
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
@@ -735,143 +481,11 @@ packages:
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
- '@typescript-eslint/eslint-plugin@8.31.1':
- resolution: {integrity: sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
- eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
-
- '@typescript-eslint/parser@8.31.1':
- resolution: {integrity: sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
-
- '@typescript-eslint/scope-manager@8.31.1':
- resolution: {integrity: sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@typescript-eslint/type-utils@8.31.1':
- resolution: {integrity: sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
-
- '@typescript-eslint/types@8.31.1':
- resolution: {integrity: sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@typescript-eslint/typescript-estree@8.31.1':
- resolution: {integrity: sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- typescript: '>=4.8.4 <5.9.0'
-
- '@typescript-eslint/utils@8.31.1':
- resolution: {integrity: sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
-
- '@typescript-eslint/visitor-keys@8.31.1':
- resolution: {integrity: sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@typescript/vfs@1.6.1':
resolution: {integrity: sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==}
peerDependencies:
typescript: '*'
- '@unrs/resolver-binding-darwin-arm64@1.7.2':
- resolution: {integrity: sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==}
- cpu: [arm64]
- os: [darwin]
-
- '@unrs/resolver-binding-darwin-x64@1.7.2':
- resolution: {integrity: sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==}
- cpu: [x64]
- os: [darwin]
-
- '@unrs/resolver-binding-freebsd-x64@1.7.2':
- resolution: {integrity: sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==}
- cpu: [x64]
- os: [freebsd]
-
- '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.2':
- resolution: {integrity: sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==}
- cpu: [arm]
- os: [linux]
-
- '@unrs/resolver-binding-linux-arm-musleabihf@1.7.2':
- resolution: {integrity: sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==}
- cpu: [arm]
- os: [linux]
-
- '@unrs/resolver-binding-linux-arm64-gnu@1.7.2':
- resolution: {integrity: sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==}
- cpu: [arm64]
- os: [linux]
-
- '@unrs/resolver-binding-linux-arm64-musl@1.7.2':
- resolution: {integrity: sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==}
- cpu: [arm64]
- os: [linux]
-
- '@unrs/resolver-binding-linux-ppc64-gnu@1.7.2':
- resolution: {integrity: sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==}
- cpu: [ppc64]
- os: [linux]
-
- '@unrs/resolver-binding-linux-riscv64-gnu@1.7.2':
- resolution: {integrity: sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==}
- cpu: [riscv64]
- os: [linux]
-
- '@unrs/resolver-binding-linux-riscv64-musl@1.7.2':
- resolution: {integrity: sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==}
- cpu: [riscv64]
- os: [linux]
-
- '@unrs/resolver-binding-linux-s390x-gnu@1.7.2':
- resolution: {integrity: sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==}
- cpu: [s390x]
- os: [linux]
-
- '@unrs/resolver-binding-linux-x64-gnu@1.7.2':
- resolution: {integrity: sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==}
- cpu: [x64]
- os: [linux]
-
- '@unrs/resolver-binding-linux-x64-musl@1.7.2':
- resolution: {integrity: sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==}
- cpu: [x64]
- os: [linux]
-
- '@unrs/resolver-binding-wasm32-wasi@1.7.2':
- resolution: {integrity: sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==}
- engines: {node: '>=14.0.0'}
- cpu: [wasm32]
-
- '@unrs/resolver-binding-win32-arm64-msvc@1.7.2':
- resolution: {integrity: sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==}
- cpu: [arm64]
- os: [win32]
-
- '@unrs/resolver-binding-win32-ia32-msvc@1.7.2':
- resolution: {integrity: sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==}
- cpu: [ia32]
- os: [win32]
-
- '@unrs/resolver-binding-win32-x64-msvc@1.7.2':
- resolution: {integrity: sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==}
- cpu: [x64]
- os: [win32]
-
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
@@ -904,49 +518,6 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
- aria-query@5.3.2:
- resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
- engines: {node: '>= 0.4'}
-
- array-buffer-byte-length@1.0.2:
- resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
- engines: {node: '>= 0.4'}
-
- array-includes@3.1.8:
- resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
- engines: {node: '>= 0.4'}
-
- array.prototype.findlast@1.2.5:
- resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
- engines: {node: '>= 0.4'}
-
- array.prototype.findlastindex@1.2.6:
- resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
- engines: {node: '>= 0.4'}
-
- array.prototype.flat@1.3.3:
- resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
- engines: {node: '>= 0.4'}
-
- array.prototype.flatmap@1.3.3:
- resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
- engines: {node: '>= 0.4'}
-
- array.prototype.tosorted@1.1.4:
- resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
- engines: {node: '>= 0.4'}
-
- arraybuffer.prototype.slice@1.0.4:
- resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
- engines: {node: '>= 0.4'}
-
- ast-types-flow@0.0.8:
- resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
-
- async-function@1.0.0:
- resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
- engines: {node: '>= 0.4'}
-
async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
@@ -960,18 +531,6 @@ packages:
peerDependencies:
postcss: ^8.1.0
- available-typed-arrays@1.0.7:
- resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
- engines: {node: '>= 0.4'}
-
- axe-core@4.10.3:
- resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
- engines: {node: '>=4'}
-
- axobject-query@4.1.0:
- resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
- engines: {node: '>= 0.4'}
-
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -981,13 +540,6 @@ packages:
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
- brace-expansion@2.0.1:
- resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
-
- braces@3.0.3:
- resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
- engines: {node: '>=8'}
-
browserslist@4.24.4:
resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -1000,22 +552,10 @@ packages:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
engines: {node: '>=18'}
- busboy@1.6.0:
- resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
- engines: {node: '>=10.16.0'}
-
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
- call-bind@1.0.8:
- resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
- engines: {node: '>= 0.4'}
-
- call-bound@1.0.4:
- resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
- engines: {node: '>= 0.4'}
-
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
@@ -1024,8 +564,8 @@ packages:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
- caniuse-lite@1.0.30001715:
- resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==}
+ caniuse-lite@1.0.30001716:
+ resolution: {integrity: sha512-49/c1+x3Kwz7ZIWt+4DvK3aMJy9oYXXG6/97JKsnjdCk/6n9vVyWL8NAwVt95Lwt9eigI10Hl782kDfZUUlRXw==}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
@@ -1039,16 +579,6 @@ packages:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
- class-variance-authority@0.7.1:
- resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
-
- client-only@0.0.1:
- resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
-
- clsx@2.1.1:
- resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
- engines: {node: '>=6'}
-
color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
@@ -1068,10 +598,6 @@ packages:
color@3.2.1:
resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
- color@4.2.3:
- resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
- engines: {node: '>=12.5.0'}
-
colors@1.4.0:
resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==}
engines: {node: '>=0.1.90'}
@@ -1110,32 +636,6 @@ packages:
engines: {node: '>=4'}
hasBin: true
- csstype@3.1.3:
- resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
-
- damerau-levenshtein@1.0.8:
- resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
-
- data-view-buffer@1.0.2:
- resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
- engines: {node: '>= 0.4'}
-
- data-view-byte-length@1.0.2:
- resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
- engines: {node: '>= 0.4'}
-
- data-view-byte-offset@1.0.1:
- resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
- engines: {node: '>= 0.4'}
-
- debug@3.2.7:
- resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
@@ -1168,18 +668,10 @@ packages:
resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
engines: {node: '>=18'}
- define-data-property@1.1.4:
- resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
- engines: {node: '>= 0.4'}
-
define-lazy-prop@3.0.0:
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
engines: {node: '>=12'}
- define-properties@1.2.1:
- resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
- engines: {node: '>= 0.4'}
-
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -1188,10 +680,6 @@ packages:
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'}
- doctrine@2.1.0:
- resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
- engines: {node: '>=0.10.0'}
-
dotenv@16.5.0:
resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==}
engines: {node: '>=12'}
@@ -1203,9 +691,6 @@ packages:
electron-to-chromium@1.5.148:
resolution: {integrity: sha512-8uc1QXwwqayD4mblcsQYZqoi+cOc97A2XmKSBOIRbEAvbp6vrqmSYs4dHD2qVygUgn7Mi0qdKgPaJ9WC8cv63A==}
- emoji-regex@9.2.2:
- resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
-
enabled@2.0.0:
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
@@ -1216,10 +701,6 @@ packages:
resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
engines: {node: '>=10.13.0'}
- es-abstract@1.23.9:
- resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
- engines: {node: '>= 0.4'}
-
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
@@ -1228,10 +709,6 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
- es-iterator-helpers@1.2.1:
- resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
- engines: {node: '>= 0.4'}
-
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
@@ -1240,14 +717,6 @@ packages:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
- es-shim-unscopables@1.1.0:
- resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
- engines: {node: '>= 0.4'}
-
- es-to-primitive@1.3.0:
- resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
- engines: {node: '>= 0.4'}
-
esbuild-plugin-tailwindcss@2.0.1:
resolution: {integrity: sha512-62CPYzyfcRE7OowGmWGKs9sz43QhCa/dZ5h6ruZhDg65B5Zsn++4EA4NKIwEMbAio9JV8+FJZNXzejNX/RjSkg==}
@@ -1264,80 +733,6 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
- eslint-config-next@15.3.1:
- resolution: {integrity: sha512-GnmyVd9TE/Ihe3RrvcafFhXErErtr2jS0JDeCSp3vWvy86AXwHsRBt0E3MqP/m8ACS1ivcsi5uaqjbhsG18qKw==}
- peerDependencies:
- eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
- typescript: '>=3.3.1'
- peerDependenciesMeta:
- typescript:
- optional: true
-
- eslint-import-resolver-node@0.3.9:
- resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
-
- eslint-import-resolver-typescript@3.10.1:
- resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==}
- engines: {node: ^14.18.0 || >=16.0.0}
- peerDependencies:
- eslint: '*'
- eslint-plugin-import: '*'
- eslint-plugin-import-x: '*'
- peerDependenciesMeta:
- eslint-plugin-import:
- optional: true
- eslint-plugin-import-x:
- optional: true
-
- eslint-module-utils@2.12.0:
- resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
- engines: {node: '>=4'}
- peerDependencies:
- '@typescript-eslint/parser': '*'
- eslint: '*'
- eslint-import-resolver-node: '*'
- eslint-import-resolver-typescript: '*'
- eslint-import-resolver-webpack: '*'
- peerDependenciesMeta:
- '@typescript-eslint/parser':
- optional: true
- eslint:
- optional: true
- eslint-import-resolver-node:
- optional: true
- eslint-import-resolver-typescript:
- optional: true
- eslint-import-resolver-webpack:
- optional: true
-
- eslint-plugin-import@2.31.0:
- resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
- engines: {node: '>=4'}
- peerDependencies:
- '@typescript-eslint/parser': '*'
- eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
- peerDependenciesMeta:
- '@typescript-eslint/parser':
- optional: true
-
- eslint-plugin-jsx-a11y@6.10.2:
- resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==}
- engines: {node: '>=4.0'}
- peerDependencies:
- eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
-
- eslint-plugin-react-hooks@5.2.0:
- resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==}
- engines: {node: '>=10'}
- peerDependencies:
- eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
-
- eslint-plugin-react@7.37.5:
- resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
- engines: {node: '>=4'}
- peerDependencies:
- eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
-
eslint-scope@8.3.0:
resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -1403,34 +798,15 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
- fast-glob@3.3.1:
- resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
- engines: {node: '>=8.6.0'}
-
- fast-glob@3.3.3:
- resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
- engines: {node: '>=8.6.0'}
-
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
- fastq@1.19.1:
- resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
-
fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
- fdir@6.4.4:
- resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==}
- peerDependencies:
- picomatch: ^3 || ^4
- peerDependenciesMeta:
- picomatch:
- optional: true
-
fecha@4.2.3:
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
@@ -1442,10 +818,6 @@ packages:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
- fill-range@7.1.1:
- resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
- engines: {node: '>=8'}
-
find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
@@ -1460,10 +832,6 @@ packages:
fn.name@1.1.0:
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
- for-each@0.3.5:
- resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
- engines: {node: '>= 0.4'}
-
form-data-encoder@1.7.2:
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
@@ -1486,13 +854,6 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
- function.prototype.name@1.1.8:
- resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
- engines: {node: '>= 0.4'}
-
- functions-have-names@1.2.3:
- resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
-
generic-names@4.0.0:
resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==}
@@ -1512,17 +873,9 @@ packages:
resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
engines: {node: '>=18'}
- get-symbol-description@1.1.0:
- resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
- engines: {node: '>= 0.4'}
-
get-tsconfig@4.10.0:
resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==}
- glob-parent@5.1.2:
- resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
- engines: {node: '>= 6'}
-
glob-parent@6.0.2:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
@@ -1531,10 +884,6 @@ packages:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
- globalthis@1.0.4:
- resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
- engines: {node: '>= 0.4'}
-
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
@@ -1542,24 +891,10 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
- graphemer@1.4.0:
- resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
-
- has-bigints@1.1.0:
- resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
- engines: {node: '>= 0.4'}
-
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
- has-property-descriptors@1.0.2:
- resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
-
- has-proto@1.2.0:
- resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
- engines: {node: '>= 0.4'}
-
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
@@ -1604,48 +939,9 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
- internal-slot@1.1.0:
- resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
- engines: {node: '>= 0.4'}
-
- is-array-buffer@3.0.5:
- resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
- engines: {node: '>= 0.4'}
-
is-arrayish@0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
- is-async-function@2.1.1:
- resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
- engines: {node: '>= 0.4'}
-
- is-bigint@1.1.0:
- resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
- engines: {node: '>= 0.4'}
-
- is-boolean-object@1.2.2:
- resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
- engines: {node: '>= 0.4'}
-
- is-bun-module@2.0.0:
- resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==}
-
- is-callable@1.2.7:
- resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
- engines: {node: '>= 0.4'}
-
- is-core-module@2.16.1:
- resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
- engines: {node: '>= 0.4'}
-
- is-data-view@1.0.2:
- resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
- engines: {node: '>= 0.4'}
-
- is-date-object@1.1.0:
- resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
- engines: {node: '>= 0.4'}
-
is-docker@3.0.0:
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -1655,14 +951,6 @@ packages:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
- is-finalizationregistry@1.1.1:
- resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
- engines: {node: '>= 0.4'}
-
- is-generator-function@1.1.0:
- resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
- engines: {node: '>= 0.4'}
-
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
@@ -1672,34 +960,10 @@ packages:
engines: {node: '>=14.16'}
hasBin: true
- is-map@2.0.3:
- resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
- engines: {node: '>= 0.4'}
-
- is-number-object@1.1.1:
- resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
- engines: {node: '>= 0.4'}
-
- is-number@7.0.0:
- resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
- engines: {node: '>=0.12.0'}
-
is-plain-obj@4.1.0:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
- is-regex@1.2.1:
- resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
- engines: {node: '>= 0.4'}
-
- is-set@2.0.3:
- resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
- engines: {node: '>= 0.4'}
-
- is-shared-array-buffer@1.0.4:
- resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
- engines: {node: '>= 0.4'}
-
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
@@ -1708,34 +972,10 @@ packages:
resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
engines: {node: '>=18'}
- is-string@1.1.1:
- resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
- engines: {node: '>= 0.4'}
-
- is-symbol@1.1.1:
- resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
- engines: {node: '>= 0.4'}
-
- is-typed-array@1.1.15:
- resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
- engines: {node: '>= 0.4'}
-
is-unicode-supported@2.1.0:
resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
engines: {node: '>=18'}
- is-weakmap@2.0.2:
- resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
- engines: {node: '>= 0.4'}
-
- is-weakref@1.1.1:
- resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
- engines: {node: '>= 0.4'}
-
- is-weakset@2.0.4:
- resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
- engines: {node: '>= 0.4'}
-
is-what@4.1.16:
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
engines: {node: '>=12.13'}
@@ -1744,16 +984,9 @@ packages:
resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
engines: {node: '>=16'}
- isarray@2.0.5:
- resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
-
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
- iterator.prototype@1.1.5:
- resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
- engines: {node: '>= 0.4'}
-
jiti@2.4.2:
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
hasBin: true
@@ -1777,18 +1010,10 @@ packages:
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
- json5@1.0.2:
- resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
- hasBin: true
-
jsonpointer@5.0.1:
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
engines: {node: '>=0.10.0'}
- jsx-ast-utils@3.3.5:
- resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
- engines: {node: '>=4.0'}
-
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@@ -1861,21 +1086,14 @@ packages:
openai:
optional: true
- langsmith@0.3.22:
- resolution: {integrity: sha512-WddAEl+++fbOF8IFpw9WmKdDN9/ec79vI58J9LcVqcVbuif+4lsuzhYwbpfN1kwNUfjg4yDcKbwlq9eFVLS4dg==}
+ langsmith@0.3.24:
+ resolution: {integrity: sha512-fPpJNHmELr3IrUau3YXgjWZI8GcZiK+lLw5BAV5ZhQYEUvcMSVI+cLqfhTnCcU6FTzr5VeJSodt8tA4n7K1dDg==}
peerDependencies:
openai: '*'
peerDependenciesMeta:
openai:
optional: true
- language-subtag-registry@0.3.23:
- resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
-
- language-tags@1.0.9:
- resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
- engines: {node: '>=0.10'}
-
levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
@@ -1962,27 +1180,10 @@ packages:
resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==}
engines: {node: '>= 12.0.0'}
- loose-envify@1.4.0:
- resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
- hasBin: true
-
- lucide-react@0.503.0:
- resolution: {integrity: sha512-HGGkdlPWQ0vTF8jJ5TdIqhQXZi6uh3LnNgfZ8MHiuxFfX3RZeA79r2MW2tHAZKlAVfoNE8esm3p+O6VkIvpj6w==}
- peerDependencies:
- react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
-
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
- merge2@1.4.1:
- resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
- engines: {node: '>= 8'}
-
- micromatch@4.0.8:
- resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
- engines: {node: '>=8.6'}
-
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@@ -1994,13 +1195,6 @@ packages:
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
- minimatch@9.0.5:
- resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
- engines: {node: '>=16 || 14 >=14.17'}
-
- minimist@1.2.8:
- resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
-
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -2026,35 +1220,9 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
- napi-postinstall@0.2.3:
- resolution: {integrity: sha512-Mi7JISo/4Ij2tDZ2xBE2WH+/KvVlkhA6juEjpEeRAVPNCpN3nxJo/5FhDNKgBcdmcmhaH6JjgST4xY/23ZYK0w==}
- engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
- hasBin: true
-
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
- next@15.3.1:
- resolution: {integrity: sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g==}
- engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
- hasBin: true
- peerDependencies:
- '@opentelemetry/api': ^1.1.0
- '@playwright/test': ^1.41.2
- babel-plugin-react-compiler: '*'
- react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
- react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
- sass: ^1.3.0
- peerDependenciesMeta:
- '@opentelemetry/api':
- optional: true
- '@playwright/test':
- optional: true
- babel-plugin-react-compiler:
- optional: true
- sass:
- optional: true
-
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
@@ -2080,38 +1248,6 @@ packages:
resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
engines: {node: '>=18'}
- object-assign@4.1.1:
- resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
- engines: {node: '>=0.10.0'}
-
- object-inspect@1.13.4:
- resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
- engines: {node: '>= 0.4'}
-
- object-keys@1.1.1:
- resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
- engines: {node: '>= 0.4'}
-
- object.assign@4.1.7:
- resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
- engines: {node: '>= 0.4'}
-
- object.entries@1.1.9:
- resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
- engines: {node: '>= 0.4'}
-
- object.fromentries@2.0.8:
- resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
- engines: {node: '>= 0.4'}
-
- object.groupby@1.0.3:
- resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
- engines: {node: '>= 0.4'}
-
- object.values@1.2.1:
- resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
- engines: {node: '>= 0.4'}
-
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@@ -2141,10 +1277,6 @@ packages:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
- own-keys@1.0.1:
- resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
- engines: {node: '>= 0.4'}
-
p-finally@1.0.0:
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
engines: {node: '>=4'}
@@ -2189,27 +1321,12 @@ packages:
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
engines: {node: '>=12'}
- path-parse@1.0.7:
- resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
-
pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
- picomatch@2.3.1:
- resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
- engines: {node: '>=8.6'}
-
- picomatch@4.0.2:
- resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
- engines: {node: '>=12'}
-
- possible-typed-array-names@1.1.0:
- resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
- engines: {node: '>= 0.4'}
-
postcss-modules-extract-imports@3.1.0:
resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==}
engines: {node: ^10 || ^12 || >= 14}
@@ -2246,10 +1363,6 @@ packages:
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
- postcss@8.4.31:
- resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
- engines: {node: ^10 || ^12 || >=14}
-
postcss@8.5.3:
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
engines: {node: ^10 || ^12 || >=14}
@@ -2262,9 +1375,6 @@ packages:
resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==}
engines: {node: '>=18'}
- prop-types@15.8.1:
- resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
-
pump@3.0.2:
resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==}
@@ -2272,21 +1382,6 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
- queue-microtask@1.2.3:
- resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
-
- react-dom@19.1.0:
- resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
- peerDependencies:
- react: ^19.1.0
-
- react-is@16.13.1:
- resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
-
- react@19.1.0:
- resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
- engines: {node: '>=0.10.0'}
-
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
@@ -2295,14 +1390,6 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
- reflect.getprototypeof@1.0.10:
- resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
- engines: {node: '>= 0.4'}
-
- regexp.prototype.flags@1.5.4:
- resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
- engines: {node: '>= 0.4'}
-
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -2310,77 +1397,26 @@ packages:
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
- resolve@1.22.10:
- resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
- engines: {node: '>= 0.4'}
- hasBin: true
-
- resolve@2.0.0-next.5:
- resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
- hasBin: true
-
retry@0.13.1:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
- reusify@1.1.0:
- resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
- engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
-
run-applescript@7.0.0:
resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
engines: {node: '>=18'}
- run-parallel@1.2.0:
- resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
-
- safe-array-concat@1.1.3:
- resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
- engines: {node: '>=0.4'}
-
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
- safe-push-apply@1.0.0:
- resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
- engines: {node: '>= 0.4'}
-
- safe-regex-test@1.1.0:
- resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
- engines: {node: '>= 0.4'}
-
safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
- scheduler@0.26.0:
- resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
-
- semver@6.3.1:
- resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
- hasBin: true
-
semver@7.7.1:
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
engines: {node: '>=10'}
hasBin: true
- set-function-length@1.2.2:
- resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
- engines: {node: '>= 0.4'}
-
- set-function-name@2.0.2:
- resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
- engines: {node: '>= 0.4'}
-
- set-proto@1.0.0:
- resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
- engines: {node: '>= 0.4'}
-
- sharp@0.34.1:
- resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==}
- engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
-
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -2389,22 +1425,6 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
- side-channel-list@1.0.0:
- resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
- engines: {node: '>= 0.4'}
-
- side-channel-map@1.0.1:
- resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
- engines: {node: '>= 0.4'}
-
- side-channel-weakmap@1.0.2:
- resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
- engines: {node: '>= 0.4'}
-
- side-channel@1.1.0:
- resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
- engines: {node: '>= 0.4'}
-
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
@@ -2419,9 +1439,6 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
- stable-hash@0.0.5:
- resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
-
stack-trace@0.0.10:
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
@@ -2429,43 +1446,12 @@ packages:
resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==}
engines: {node: '>=6'}
- streamsearch@1.1.0:
- resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
- engines: {node: '>=10.0.0'}
-
string-hash@1.1.3:
resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
- string.prototype.includes@2.0.1:
- resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
- engines: {node: '>= 0.4'}
-
- string.prototype.matchall@4.0.12:
- resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
- engines: {node: '>= 0.4'}
-
- string.prototype.repeat@1.0.0:
- resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
-
- string.prototype.trim@1.2.10:
- resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
- engines: {node: '>= 0.4'}
-
- string.prototype.trimend@1.0.9:
- resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
- engines: {node: '>= 0.4'}
-
- string.prototype.trimstart@1.0.8:
- resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
- engines: {node: '>= 0.4'}
-
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
- strip-bom@3.0.0:
- resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
- engines: {node: '>=4'}
-
strip-final-newline@4.0.0:
resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
engines: {node: '>=18'}
@@ -2474,19 +1460,6 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
- styled-jsx@5.1.6:
- resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
- engines: {node: '>= 12.0.0'}
- peerDependencies:
- '@babel/core': '*'
- babel-plugin-macros: '*'
- react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
- peerDependenciesMeta:
- '@babel/core':
- optional: true
- babel-plugin-macros:
- optional: true
-
superjson@2.2.2:
resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
engines: {node: '>=16'}
@@ -2495,15 +1468,8 @@ packages:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
- supports-preserve-symlinks-flag@1.0.0:
- resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
- engines: {node: '>= 0.4'}
-
- tailwind-merge@3.2.0:
- resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==}
-
- tailwindcss@4.1.4:
- resolution: {integrity: sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==}
+ tailwindcss@4.1.5:
+ resolution: {integrity: sha512-nYtSPfWGDiWgCkwQG/m+aX83XCwf62sBgg3bIlNiiOcggnS1x3uVRDAuyelBFL+vJdOPPCGElxv9DjHJjRHiVA==}
tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
@@ -2516,14 +1482,6 @@ packages:
text-hex@1.0.0:
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
- tinyglobby@0.2.13:
- resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
- engines: {node: '>=12.0.0'}
-
- to-regex-range@5.0.1:
- resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
- engines: {node: '>=8.0'}
-
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
@@ -2531,26 +1489,11 @@ packages:
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
engines: {node: '>= 14.0.0'}
- ts-api-utils@2.1.0:
- resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
- engines: {node: '>=18.12'}
- peerDependencies:
- typescript: '>=4.8.4'
-
- tsconfig-paths@3.15.0:
- resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
-
- tslib@2.8.1:
- resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
-
tsx@4.19.4:
resolution: {integrity: sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==}
engines: {node: '>=18.0.0'}
hasBin: true
- tw-animate-css@1.2.8:
- resolution: {integrity: sha512-AxSnYRvyFnAiZCUndS3zQZhNfV/B77ZhJ+O7d3K6wfg/jKJY+yv6ahuyXwnyaYA9UdLqnpCwhTRv9pPTBnPR2g==}
-
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -2559,31 +1502,11 @@ packages:
resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==}
engines: {node: '>=8'}
- typed-array-buffer@1.0.3:
- resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
- engines: {node: '>= 0.4'}
-
- typed-array-byte-length@1.0.3:
- resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
- engines: {node: '>= 0.4'}
-
- typed-array-byte-offset@1.0.4:
- resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
- engines: {node: '>= 0.4'}
-
- typed-array-length@1.0.7:
- resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
- engines: {node: '>= 0.4'}
-
typescript@5.8.3:
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
engines: {node: '>=14.17'}
hasBin: true
- unbox-primitive@1.1.0:
- resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
- engines: {node: '>= 0.4'}
-
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
@@ -2594,9 +1517,6 @@ packages:
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
engines: {node: '>=18'}
- unrs-resolver@1.7.2:
- resolution: {integrity: sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==}
-
update-browserslist-db@1.1.3:
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
hasBin: true
@@ -2627,22 +1547,6 @@ packages:
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
- which-boxed-primitive@1.1.1:
- resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
- engines: {node: '>= 0.4'}
-
- which-builtin-type@1.2.1:
- resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
- engines: {node: '>= 0.4'}
-
- which-collection@1.0.2:
- resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
- engines: {node: '>= 0.4'}
-
- which-typed-array@1.1.19:
- resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
- engines: {node: '>= 0.4'}
-
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -2720,22 +1624,6 @@ snapshots:
enabled: 2.0.0
kuler: 2.0.0
- '@emnapi/core@1.4.3':
- dependencies:
- '@emnapi/wasi-threads': 1.0.2
- tslib: 2.8.1
- optional: true
-
- '@emnapi/runtime@1.4.3':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@emnapi/wasi-threads@1.0.2':
- dependencies:
- tslib: 2.8.1
- optional: true
-
'@esbuild/aix-ppc64@0.25.3':
optional: true
@@ -2826,7 +1714,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/config-helpers@0.2.1': {}
+ '@eslint/config-helpers@0.2.2': {}
'@eslint/core@0.13.0':
dependencies:
@@ -2877,96 +1765,18 @@ snapshots:
'@humanwhocodes/retry@0.4.2': {}
- '@img/sharp-darwin-arm64@0.34.1':
- optionalDependencies:
- '@img/sharp-libvips-darwin-arm64': 1.1.0
- optional: true
-
- '@img/sharp-darwin-x64@0.34.1':
- optionalDependencies:
- '@img/sharp-libvips-darwin-x64': 1.1.0
- optional: true
-
- '@img/sharp-libvips-darwin-arm64@1.1.0':
- optional: true
-
- '@img/sharp-libvips-darwin-x64@1.1.0':
- optional: true
-
- '@img/sharp-libvips-linux-arm64@1.1.0':
- optional: true
-
- '@img/sharp-libvips-linux-arm@1.1.0':
- optional: true
-
- '@img/sharp-libvips-linux-ppc64@1.1.0':
- optional: true
-
- '@img/sharp-libvips-linux-s390x@1.1.0':
- optional: true
-
- '@img/sharp-libvips-linux-x64@1.1.0':
- optional: true
-
- '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
- optional: true
-
- '@img/sharp-libvips-linuxmusl-x64@1.1.0':
- optional: true
-
- '@img/sharp-linux-arm64@0.34.1':
- optionalDependencies:
- '@img/sharp-libvips-linux-arm64': 1.1.0
- optional: true
-
- '@img/sharp-linux-arm@0.34.1':
- optionalDependencies:
- '@img/sharp-libvips-linux-arm': 1.1.0
- optional: true
-
- '@img/sharp-linux-s390x@0.34.1':
- optionalDependencies:
- '@img/sharp-libvips-linux-s390x': 1.1.0
- optional: true
-
- '@img/sharp-linux-x64@0.34.1':
- optionalDependencies:
- '@img/sharp-libvips-linux-x64': 1.1.0
- optional: true
-
- '@img/sharp-linuxmusl-arm64@0.34.1':
- optionalDependencies:
- '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
- optional: true
-
- '@img/sharp-linuxmusl-x64@0.34.1':
- optionalDependencies:
- '@img/sharp-libvips-linuxmusl-x64': 1.1.0
- optional: true
-
- '@img/sharp-wasm32@0.34.1':
- dependencies:
- '@emnapi/runtime': 1.4.3
- optional: true
-
- '@img/sharp-win32-ia32@0.34.1':
- optional: true
-
- '@img/sharp-win32-x64@0.34.1':
- optional: true
-
'@isaacs/fs-minipass@4.0.1':
dependencies:
minipass: 7.1.2
- '@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3))':
+ '@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3))':
dependencies:
'@cfworker/json-schema': 4.1.1
ansi-styles: 5.2.0
camelcase: 6.3.0
decamelize: 1.2.0
js-tiktoken: 1.0.20
- langsmith: 0.3.22(openai@4.96.2(zod@3.24.3))
+ langsmith: 0.3.24(openai@4.96.2(zod@3.24.3))
mustache: 4.2.0
p-queue: 6.6.2
p-retry: 4.6.2
@@ -2976,14 +1786,14 @@ snapshots:
transitivePeerDependencies:
- openai
- '@langchain/langgraph-api@0.0.30(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3))))(@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0))(@langchain/langgraph@0.2.67(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)(zod-to-json-schema@3.24.5(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))(typescript@5.8.3)':
+ '@langchain/langgraph-api@0.0.30(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3))))(@langchain/langgraph@0.2.67(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(zod-to-json-schema@3.24.5(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))(typescript@5.8.3)':
dependencies:
'@babel/code-frame': 7.27.1
'@hono/node-server': 1.14.1(hono@4.7.8)
'@hono/zod-validator': 0.2.2(hono@4.7.8)(zod@3.24.3)
- '@langchain/core': 0.3.49(openai@4.96.2(zod@3.24.3))
- '@langchain/langgraph': 0.2.67(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)(zod-to-json-schema@3.24.5(zod@3.24.3))
- '@langchain/langgraph-checkpoint': 0.0.17(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))
+ '@langchain/core': 0.3.50(openai@4.96.2(zod@3.24.3))
+ '@langchain/langgraph': 0.2.67(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(zod-to-json-schema@3.24.5(zod@3.24.3))
+ '@langchain/langgraph-checkpoint': 0.0.17(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))
'@langchain/langgraph-ui': 0.0.30
'@types/json-schema': 7.0.15
'@typescript/vfs': 1.6.1(typescript@5.8.3)
@@ -3002,23 +1812,21 @@ snapshots:
winston: 3.17.0
winston-console-format: 1.0.8
zod: 3.24.3
- optionalDependencies:
- '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)
transitivePeerDependencies:
- babel-plugin-macros
- openai
- supports-color
- '@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))':
+ '@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))':
dependencies:
- '@langchain/core': 0.3.49(openai@4.96.2(zod@3.24.3))
+ '@langchain/core': 0.3.50(openai@4.96.2(zod@3.24.3))
uuid: 10.0.0
- '@langchain/langgraph-cli@0.0.30(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3))))(@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0))(@langchain/langgraph@0.2.67(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)(zod-to-json-schema@3.24.5(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))(typescript@5.8.3)':
+ '@langchain/langgraph-cli@0.0.30(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3))))(@langchain/langgraph@0.2.67(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(zod-to-json-schema@3.24.5(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))(typescript@5.8.3)':
dependencies:
'@babel/code-frame': 7.27.1
'@commander-js/extra-typings': 13.1.0(commander@13.1.0)
- '@langchain/langgraph-api': 0.0.30(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3))))(@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0))(@langchain/langgraph@0.2.67(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)(zod-to-json-schema@3.24.5(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))(typescript@5.8.3)
+ '@langchain/langgraph-api': 0.0.30(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3))))(@langchain/langgraph@0.2.67(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(zod-to-json-schema@3.24.5(zod@3.24.3)))(openai@4.96.2(zod@3.24.3))(typescript@5.8.3)
chokidar: 4.0.3
commander: 13.1.0
dedent: 1.6.0
@@ -3044,15 +1852,14 @@ snapshots:
- supports-color
- typescript
- '@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)':
+ '@langchain/langgraph-sdk@0.0.74(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))':
dependencies:
'@types/json-schema': 7.0.15
p-queue: 6.6.2
p-retry: 4.6.2
uuid: 9.0.1
optionalDependencies:
- '@langchain/core': 0.3.49(openai@4.96.2(zod@3.24.3))
- react: 19.1.0
+ '@langchain/core': 0.3.50(openai@4.96.2(zod@3.24.3))
'@langchain/langgraph-ui@0.0.30':
dependencies:
@@ -3062,11 +1869,11 @@ snapshots:
esbuild-plugin-tailwindcss: 2.0.1
zod: 3.24.3
- '@langchain/langgraph@0.2.67(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)(zod-to-json-schema@3.24.5(zod@3.24.3))':
+ '@langchain/langgraph@0.2.67(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(zod-to-json-schema@3.24.5(zod@3.24.3))':
dependencies:
- '@langchain/core': 0.3.49(openai@4.96.2(zod@3.24.3))
- '@langchain/langgraph-checkpoint': 0.0.17(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))
- '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(react@19.1.0)
+ '@langchain/core': 0.3.50(openai@4.96.2(zod@3.24.3))
+ '@langchain/langgraph-checkpoint': 0.0.17(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))
+ '@langchain/langgraph-sdk': 0.0.74(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))
uuid: 10.0.0
zod: 3.24.3
optionalDependencies:
@@ -3074,9 +1881,9 @@ snapshots:
transitivePeerDependencies:
- react
- '@langchain/openai@0.5.7(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))':
+ '@langchain/openai@0.5.8(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))':
dependencies:
- '@langchain/core': 0.3.49(openai@4.96.2(zod@3.24.3))
+ '@langchain/core': 0.3.50(openai@4.96.2(zod@3.24.3))
js-tiktoken: 1.0.20
openai: 4.96.2(zod@3.24.3)
zod: 3.24.3
@@ -3085,153 +1892,85 @@ snapshots:
- encoding
- ws
- '@langchain/textsplitters@0.1.0(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))':
+ '@langchain/textsplitters@0.1.0(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))':
dependencies:
- '@langchain/core': 0.3.49(openai@4.96.2(zod@3.24.3))
+ '@langchain/core': 0.3.50(openai@4.96.2(zod@3.24.3))
js-tiktoken: 1.0.20
- '@napi-rs/wasm-runtime@0.2.9':
- dependencies:
- '@emnapi/core': 1.4.3
- '@emnapi/runtime': 1.4.3
- '@tybys/wasm-util': 0.9.0
- optional: true
-
- '@next/env@15.3.1': {}
-
- '@next/eslint-plugin-next@15.3.1':
- dependencies:
- fast-glob: 3.3.1
-
- '@next/swc-darwin-arm64@15.3.1':
- optional: true
-
- '@next/swc-darwin-x64@15.3.1':
- optional: true
-
- '@next/swc-linux-arm64-gnu@15.3.1':
- optional: true
-
- '@next/swc-linux-arm64-musl@15.3.1':
- optional: true
-
- '@next/swc-linux-x64-gnu@15.3.1':
- optional: true
-
- '@next/swc-linux-x64-musl@15.3.1':
- optional: true
-
- '@next/swc-win32-arm64-msvc@15.3.1':
- optional: true
-
- '@next/swc-win32-x64-msvc@15.3.1':
- optional: true
-
- '@nodelib/fs.scandir@2.1.5':
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- run-parallel: 1.2.0
-
- '@nodelib/fs.stat@2.0.5': {}
-
- '@nodelib/fs.walk@1.2.8':
- dependencies:
- '@nodelib/fs.scandir': 2.1.5
- fastq: 1.19.1
-
- '@nolyfill/is-core-module@1.0.39': {}
-
- '@rtsao/scc@1.1.0': {}
-
- '@rushstack/eslint-patch@1.11.0': {}
-
'@sec-ant/readable-stream@0.4.1': {}
'@sindresorhus/merge-streams@4.0.0': {}
- '@swc/counter@0.1.3': {}
-
- '@swc/helpers@0.5.15':
- dependencies:
- tslib: 2.8.1
-
- '@tailwindcss/node@4.1.4':
+ '@tailwindcss/node@4.1.5':
dependencies:
enhanced-resolve: 5.18.1
jiti: 2.4.2
lightningcss: 1.29.2
- tailwindcss: 4.1.4
+ tailwindcss: 4.1.5
- '@tailwindcss/oxide-android-arm64@4.1.4':
+ '@tailwindcss/oxide-android-arm64@4.1.5':
optional: true
- '@tailwindcss/oxide-darwin-arm64@4.1.4':
+ '@tailwindcss/oxide-darwin-arm64@4.1.5':
optional: true
- '@tailwindcss/oxide-darwin-x64@4.1.4':
+ '@tailwindcss/oxide-darwin-x64@4.1.5':
optional: true
- '@tailwindcss/oxide-freebsd-x64@4.1.4':
+ '@tailwindcss/oxide-freebsd-x64@4.1.5':
optional: true
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.4':
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5':
optional: true
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.4':
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.5':
optional: true
- '@tailwindcss/oxide-linux-arm64-musl@4.1.4':
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.5':
optional: true
- '@tailwindcss/oxide-linux-x64-gnu@4.1.4':
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.5':
optional: true
- '@tailwindcss/oxide-linux-x64-musl@4.1.4':
+ '@tailwindcss/oxide-linux-x64-musl@4.1.5':
optional: true
- '@tailwindcss/oxide-wasm32-wasi@4.1.4':
+ '@tailwindcss/oxide-wasm32-wasi@4.1.5':
optional: true
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.4':
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.5':
optional: true
- '@tailwindcss/oxide-win32-x64-msvc@4.1.4':
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.5':
optional: true
- '@tailwindcss/oxide@4.1.4':
+ '@tailwindcss/oxide@4.1.5':
optionalDependencies:
- '@tailwindcss/oxide-android-arm64': 4.1.4
- '@tailwindcss/oxide-darwin-arm64': 4.1.4
- '@tailwindcss/oxide-darwin-x64': 4.1.4
- '@tailwindcss/oxide-freebsd-x64': 4.1.4
- '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.4
- '@tailwindcss/oxide-linux-arm64-gnu': 4.1.4
- '@tailwindcss/oxide-linux-arm64-musl': 4.1.4
- '@tailwindcss/oxide-linux-x64-gnu': 4.1.4
- '@tailwindcss/oxide-linux-x64-musl': 4.1.4
- '@tailwindcss/oxide-wasm32-wasi': 4.1.4
- '@tailwindcss/oxide-win32-arm64-msvc': 4.1.4
- '@tailwindcss/oxide-win32-x64-msvc': 4.1.4
+ '@tailwindcss/oxide-android-arm64': 4.1.5
+ '@tailwindcss/oxide-darwin-arm64': 4.1.5
+ '@tailwindcss/oxide-darwin-x64': 4.1.5
+ '@tailwindcss/oxide-freebsd-x64': 4.1.5
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.5
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.5
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.5
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.5
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.5
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.5
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.5
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.5
- '@tailwindcss/postcss@4.1.4':
+ '@tailwindcss/postcss@4.1.5':
dependencies:
'@alloc/quick-lru': 5.2.0
- '@tailwindcss/node': 4.1.4
- '@tailwindcss/oxide': 4.1.4
+ '@tailwindcss/node': 4.1.5
+ '@tailwindcss/oxide': 4.1.5
postcss: 8.5.3
- tailwindcss: 4.1.4
-
- '@tybys/wasm-util@0.9.0':
- dependencies:
- tslib: 2.8.1
- optional: true
+ tailwindcss: 4.1.5
'@types/estree@1.0.7': {}
'@types/json-schema@7.0.15': {}
- '@types/json5@0.0.29': {}
-
'@types/node-fetch@2.6.12':
dependencies:
'@types/node': 20.17.32
@@ -3245,14 +1984,6 @@ snapshots:
dependencies:
undici-types: 6.19.8
- '@types/react-dom@19.1.2(@types/react@19.1.2)':
- dependencies:
- '@types/react': 19.1.2
-
- '@types/react@19.1.2':
- dependencies:
- csstype: 3.1.3
-
'@types/retry@0.12.0': {}
'@types/triple-beam@1.3.5': {}
@@ -3264,83 +1995,6 @@ snapshots:
'@types/node': 20.17.32
optional: true
- '@typescript-eslint/eslint-plugin@8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)':
- dependencies:
- '@eslint-community/regexpp': 4.12.1
- '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- '@typescript-eslint/scope-manager': 8.31.1
- '@typescript-eslint/type-utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- '@typescript-eslint/visitor-keys': 8.31.1
- eslint: 9.25.1(jiti@2.4.2)
- graphemer: 1.4.0
- ignore: 5.3.2
- natural-compare: 1.4.0
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)':
- dependencies:
- '@typescript-eslint/scope-manager': 8.31.1
- '@typescript-eslint/types': 8.31.1
- '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3)
- '@typescript-eslint/visitor-keys': 8.31.1
- debug: 4.4.0
- eslint: 9.25.1(jiti@2.4.2)
- typescript: 5.8.3
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/scope-manager@8.31.1':
- dependencies:
- '@typescript-eslint/types': 8.31.1
- '@typescript-eslint/visitor-keys': 8.31.1
-
- '@typescript-eslint/type-utils@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)':
- dependencies:
- '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3)
- '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- debug: 4.4.0
- eslint: 9.25.1(jiti@2.4.2)
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/types@8.31.1': {}
-
- '@typescript-eslint/typescript-estree@8.31.1(typescript@5.8.3)':
- dependencies:
- '@typescript-eslint/types': 8.31.1
- '@typescript-eslint/visitor-keys': 8.31.1
- debug: 4.4.0
- fast-glob: 3.3.3
- is-glob: 4.0.3
- minimatch: 9.0.5
- semver: 7.7.1
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/utils@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)':
- dependencies:
- '@eslint-community/eslint-utils': 4.6.1(eslint@9.25.1(jiti@2.4.2))
- '@typescript-eslint/scope-manager': 8.31.1
- '@typescript-eslint/types': 8.31.1
- '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3)
- eslint: 9.25.1(jiti@2.4.2)
- typescript: 5.8.3
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/visitor-keys@8.31.1':
- dependencies:
- '@typescript-eslint/types': 8.31.1
- eslint-visitor-keys: 4.2.0
-
'@typescript/vfs@1.6.1(typescript@5.8.3)':
dependencies:
debug: 4.4.0
@@ -3348,59 +2002,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@unrs/resolver-binding-darwin-arm64@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-darwin-x64@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-freebsd-x64@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-arm-musleabihf@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-arm64-gnu@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-arm64-musl@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-ppc64-gnu@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-riscv64-gnu@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-riscv64-musl@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-s390x-gnu@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-x64-gnu@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-linux-x64-musl@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-wasm32-wasi@1.7.2':
- dependencies:
- '@napi-rs/wasm-runtime': 0.2.9
- optional: true
-
- '@unrs/resolver-binding-win32-arm64-msvc@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-win32-ia32-msvc@1.7.2':
- optional: true
-
- '@unrs/resolver-binding-win32-x64-msvc@1.7.2':
- optional: true
-
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
@@ -3430,77 +2031,6 @@ snapshots:
argparse@2.0.1: {}
- aria-query@5.3.2: {}
-
- array-buffer-byte-length@1.0.2:
- dependencies:
- call-bound: 1.0.4
- is-array-buffer: 3.0.5
-
- array-includes@3.1.8:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-object-atoms: 1.1.1
- get-intrinsic: 1.3.0
- is-string: 1.1.1
-
- array.prototype.findlast@1.2.5:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-errors: 1.3.0
- es-object-atoms: 1.1.1
- es-shim-unscopables: 1.1.0
-
- array.prototype.findlastindex@1.2.6:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-errors: 1.3.0
- es-object-atoms: 1.1.1
- es-shim-unscopables: 1.1.0
-
- array.prototype.flat@1.3.3:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-shim-unscopables: 1.1.0
-
- array.prototype.flatmap@1.3.3:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-shim-unscopables: 1.1.0
-
- array.prototype.tosorted@1.1.4:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-errors: 1.3.0
- es-shim-unscopables: 1.1.0
-
- arraybuffer.prototype.slice@1.0.4:
- dependencies:
- array-buffer-byte-length: 1.0.2
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-errors: 1.3.0
- get-intrinsic: 1.3.0
- is-array-buffer: 3.0.5
-
- ast-types-flow@0.0.8: {}
-
- async-function@1.0.0: {}
-
async@3.2.6: {}
asynckit@0.4.0: {}
@@ -3508,21 +2038,13 @@ snapshots:
autoprefixer@10.4.21(postcss@8.5.3):
dependencies:
browserslist: 4.24.4
- caniuse-lite: 1.0.30001715
+ caniuse-lite: 1.0.30001716
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.1.1
postcss: 8.5.3
postcss-value-parser: 4.2.0
- available-typed-arrays@1.0.7:
- dependencies:
- possible-typed-array-names: 1.1.0
-
- axe-core@4.10.3: {}
-
- axobject-query@4.1.0: {}
-
balanced-match@1.0.2: {}
base64-js@1.5.1: {}
@@ -3532,17 +2054,9 @@ snapshots:
balanced-match: 1.0.2
concat-map: 0.0.1
- brace-expansion@2.0.1:
- dependencies:
- balanced-match: 1.0.2
-
- braces@3.0.3:
- dependencies:
- fill-range: 7.1.1
-
browserslist@4.24.4:
dependencies:
- caniuse-lite: 1.0.30001715
+ caniuse-lite: 1.0.30001716
electron-to-chromium: 1.5.148
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.24.4)
@@ -3553,32 +2067,16 @@ snapshots:
dependencies:
run-applescript: 7.0.0
- busboy@1.6.0:
- dependencies:
- streamsearch: 1.1.0
-
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
- call-bind@1.0.8:
- dependencies:
- call-bind-apply-helpers: 1.0.2
- es-define-property: 1.0.1
- get-intrinsic: 1.3.0
- set-function-length: 1.2.2
-
- call-bound@1.0.4:
- dependencies:
- call-bind-apply-helpers: 1.0.2
- get-intrinsic: 1.3.0
-
callsites@3.1.0: {}
camelcase@6.3.0: {}
- caniuse-lite@1.0.30001715: {}
+ caniuse-lite@1.0.30001716: {}
chalk@4.1.2:
dependencies:
@@ -3591,14 +2089,6 @@ snapshots:
chownr@3.0.0: {}
- class-variance-authority@0.7.1:
- dependencies:
- clsx: 2.1.1
-
- client-only@0.0.1: {}
-
- clsx@2.1.1: {}
-
color-convert@1.9.3:
dependencies:
color-name: 1.1.3
@@ -3621,12 +2111,6 @@ snapshots:
color-convert: 1.9.3
color-string: 1.9.1
- color@4.2.3:
- dependencies:
- color-convert: 2.0.1
- color-string: 1.9.1
- optional: true
-
colors@1.4.0: {}
colorspace@1.1.4:
@@ -3660,32 +2144,6 @@ snapshots:
cssesc@3.0.0: {}
- csstype@3.1.3: {}
-
- damerau-levenshtein@1.0.8: {}
-
- data-view-buffer@1.0.2:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-data-view: 1.0.2
-
- data-view-byte-length@1.0.2:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-data-view: 1.0.2
-
- data-view-byte-offset@1.0.1:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-data-view: 1.0.2
-
- debug@3.2.7:
- dependencies:
- ms: 2.1.3
-
debug@4.4.0:
dependencies:
ms: 2.1.3
@@ -3703,28 +2161,12 @@ snapshots:
bundle-name: 4.1.0
default-browser-id: 5.0.0
- define-data-property@1.1.4:
- dependencies:
- es-define-property: 1.0.1
- es-errors: 1.3.0
- gopd: 1.2.0
-
define-lazy-prop@3.0.0: {}
- define-properties@1.2.1:
- dependencies:
- define-data-property: 1.1.4
- has-property-descriptors: 1.0.2
- object-keys: 1.1.1
-
delayed-stream@1.0.0: {}
detect-libc@2.0.4: {}
- doctrine@2.1.0:
- dependencies:
- esutils: 2.0.3
-
dotenv@16.5.0: {}
dunder-proto@1.0.1:
@@ -3735,8 +2177,6 @@ snapshots:
electron-to-chromium@1.5.148: {}
- emoji-regex@9.2.2: {}
-
enabled@2.0.0: {}
end-of-stream@1.4.4:
@@ -3748,83 +2188,10 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.2.1
- es-abstract@1.23.9:
- dependencies:
- array-buffer-byte-length: 1.0.2
- arraybuffer.prototype.slice: 1.0.4
- available-typed-arrays: 1.0.7
- call-bind: 1.0.8
- call-bound: 1.0.4
- data-view-buffer: 1.0.2
- data-view-byte-length: 1.0.2
- data-view-byte-offset: 1.0.1
- es-define-property: 1.0.1
- es-errors: 1.3.0
- es-object-atoms: 1.1.1
- es-set-tostringtag: 2.1.0
- es-to-primitive: 1.3.0
- function.prototype.name: 1.1.8
- get-intrinsic: 1.3.0
- get-proto: 1.0.1
- get-symbol-description: 1.1.0
- globalthis: 1.0.4
- gopd: 1.2.0
- has-property-descriptors: 1.0.2
- has-proto: 1.2.0
- has-symbols: 1.1.0
- hasown: 2.0.2
- internal-slot: 1.1.0
- is-array-buffer: 3.0.5
- is-callable: 1.2.7
- is-data-view: 1.0.2
- is-regex: 1.2.1
- is-shared-array-buffer: 1.0.4
- is-string: 1.1.1
- is-typed-array: 1.1.15
- is-weakref: 1.1.1
- math-intrinsics: 1.1.0
- object-inspect: 1.13.4
- object-keys: 1.1.1
- object.assign: 4.1.7
- own-keys: 1.0.1
- regexp.prototype.flags: 1.5.4
- safe-array-concat: 1.1.3
- safe-push-apply: 1.0.0
- safe-regex-test: 1.1.0
- set-proto: 1.0.0
- string.prototype.trim: 1.2.10
- string.prototype.trimend: 1.0.9
- string.prototype.trimstart: 1.0.8
- typed-array-buffer: 1.0.3
- typed-array-byte-length: 1.0.3
- typed-array-byte-offset: 1.0.4
- typed-array-length: 1.0.7
- unbox-primitive: 1.1.0
- which-typed-array: 1.1.19
-
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
- es-iterator-helpers@1.2.1:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-errors: 1.3.0
- es-set-tostringtag: 2.1.0
- function-bind: 1.1.2
- get-intrinsic: 1.3.0
- globalthis: 1.0.4
- gopd: 1.2.0
- has-property-descriptors: 1.0.2
- has-proto: 1.2.0
- has-symbols: 1.1.0
- internal-slot: 1.1.0
- iterator.prototype: 1.1.5
- safe-array-concat: 1.1.3
-
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
@@ -3836,19 +2203,9 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.2
- es-shim-unscopables@1.1.0:
- dependencies:
- hasown: 2.0.2
-
- es-to-primitive@1.3.0:
- dependencies:
- is-callable: 1.2.7
- is-date-object: 1.1.0
- is-symbol: 1.1.1
-
esbuild-plugin-tailwindcss@2.0.1:
dependencies:
- '@tailwindcss/postcss': 4.1.4
+ '@tailwindcss/postcss': 4.1.5
autoprefixer: 10.4.21(postcss@8.5.3)
postcss: 8.5.3
postcss-modules: 6.0.1(postcss@8.5.3)
@@ -3885,134 +2242,6 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-config-next@15.3.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3):
- dependencies:
- '@next/eslint-plugin-next': 15.3.1
- '@rushstack/eslint-patch': 1.11.0
- '@typescript-eslint/eslint-plugin': 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- eslint: 9.25.1(jiti@2.4.2)
- eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.25.1(jiti@2.4.2))
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2))
- eslint-plugin-jsx-a11y: 6.10.2(eslint@9.25.1(jiti@2.4.2))
- eslint-plugin-react: 7.37.5(eslint@9.25.1(jiti@2.4.2))
- eslint-plugin-react-hooks: 5.2.0(eslint@9.25.1(jiti@2.4.2))
- optionalDependencies:
- typescript: 5.8.3
- transitivePeerDependencies:
- - eslint-import-resolver-webpack
- - eslint-plugin-import-x
- - supports-color
-
- eslint-import-resolver-node@0.3.9:
- dependencies:
- debug: 3.2.7
- is-core-module: 2.16.1
- resolve: 1.22.10
- transitivePeerDependencies:
- - supports-color
-
- eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.25.1(jiti@2.4.2)):
- dependencies:
- '@nolyfill/is-core-module': 1.0.39
- debug: 4.4.0
- eslint: 9.25.1(jiti@2.4.2)
- get-tsconfig: 4.10.0
- is-bun-module: 2.0.0
- stable-hash: 0.0.5
- tinyglobby: 0.2.13
- unrs-resolver: 1.7.2
- optionalDependencies:
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2))
- transitivePeerDependencies:
- - supports-color
-
- eslint-module-utils@2.12.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2)):
- dependencies:
- debug: 3.2.7
- optionalDependencies:
- '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- eslint: 9.25.1(jiti@2.4.2)
- eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.25.1(jiti@2.4.2))
- transitivePeerDependencies:
- - supports-color
-
- eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2)):
- dependencies:
- '@rtsao/scc': 1.1.0
- array-includes: 3.1.8
- 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.25.1(jiti@2.4.2)
- eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2))
- 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
- optionalDependencies:
- '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)
- transitivePeerDependencies:
- - eslint-import-resolver-typescript
- - eslint-import-resolver-webpack
- - supports-color
-
- eslint-plugin-jsx-a11y@6.10.2(eslint@9.25.1(jiti@2.4.2)):
- dependencies:
- aria-query: 5.3.2
- array-includes: 3.1.8
- array.prototype.flatmap: 1.3.3
- ast-types-flow: 0.0.8
- axe-core: 4.10.3
- axobject-query: 4.1.0
- damerau-levenshtein: 1.0.8
- emoji-regex: 9.2.2
- eslint: 9.25.1(jiti@2.4.2)
- hasown: 2.0.2
- jsx-ast-utils: 3.3.5
- language-tags: 1.0.9
- minimatch: 3.1.2
- object.fromentries: 2.0.8
- safe-regex-test: 1.1.0
- string.prototype.includes: 2.0.1
-
- eslint-plugin-react-hooks@5.2.0(eslint@9.25.1(jiti@2.4.2)):
- dependencies:
- eslint: 9.25.1(jiti@2.4.2)
-
- eslint-plugin-react@7.37.5(eslint@9.25.1(jiti@2.4.2)):
- dependencies:
- array-includes: 3.1.8
- array.prototype.findlast: 1.2.5
- array.prototype.flatmap: 1.3.3
- array.prototype.tosorted: 1.1.4
- doctrine: 2.1.0
- es-iterator-helpers: 1.2.1
- eslint: 9.25.1(jiti@2.4.2)
- estraverse: 5.3.0
- hasown: 2.0.2
- jsx-ast-utils: 3.3.5
- minimatch: 3.1.2
- object.entries: 1.1.9
- object.fromentries: 2.0.8
- object.values: 1.2.1
- prop-types: 15.8.1
- resolve: 2.0.0-next.5
- semver: 6.3.1
- string.prototype.matchall: 4.0.12
- string.prototype.repeat: 1.0.0
-
eslint-scope@8.3.0:
dependencies:
esrecurse: 4.3.0
@@ -4027,7 +2256,7 @@ snapshots:
'@eslint-community/eslint-utils': 4.6.1(eslint@9.25.1(jiti@2.4.2))
'@eslint-community/regexpp': 4.12.1
'@eslint/config-array': 0.20.0
- '@eslint/config-helpers': 0.2.1
+ '@eslint/config-helpers': 0.2.2
'@eslint/core': 0.13.0
'@eslint/eslintrc': 3.3.1
'@eslint/js': 9.25.1
@@ -4115,38 +2344,14 @@ snapshots:
fast-deep-equal@3.1.3: {}
- fast-glob@3.3.1:
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- '@nodelib/fs.walk': 1.2.8
- glob-parent: 5.1.2
- merge2: 1.4.1
- micromatch: 4.0.8
-
- fast-glob@3.3.3:
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- '@nodelib/fs.walk': 1.2.8
- glob-parent: 5.1.2
- merge2: 1.4.1
- micromatch: 4.0.8
-
fast-json-stable-stringify@2.1.0: {}
fast-levenshtein@2.0.6: {}
- fastq@1.19.1:
- dependencies:
- reusify: 1.1.0
-
fd-slicer@1.1.0:
dependencies:
pend: 1.2.0
- fdir@6.4.4(picomatch@4.0.2):
- optionalDependencies:
- picomatch: 4.0.2
-
fecha@4.2.3: {}
figures@6.1.0:
@@ -4157,10 +2362,6 @@ snapshots:
dependencies:
flat-cache: 4.0.1
- fill-range@7.1.1:
- dependencies:
- to-regex-range: 5.0.1
-
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
@@ -4175,10 +2376,6 @@ snapshots:
fn.name@1.1.0: {}
- for-each@0.3.5:
- dependencies:
- is-callable: 1.2.7
-
form-data-encoder@1.7.2: {}
form-data@4.0.2:
@@ -4200,17 +2397,6 @@ snapshots:
function-bind@1.1.2: {}
- function.prototype.name@1.1.8:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-properties: 1.2.1
- functions-have-names: 1.2.3
- hasown: 2.0.2
- is-callable: 1.2.7
-
- functions-have-names@1.2.3: {}
-
generic-names@4.0.0:
dependencies:
loader-utils: 3.3.1
@@ -4242,49 +2428,22 @@ snapshots:
'@sec-ant/readable-stream': 0.4.1
is-stream: 4.0.1
- get-symbol-description@1.1.0:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- get-intrinsic: 1.3.0
-
get-tsconfig@4.10.0:
dependencies:
resolve-pkg-maps: 1.0.0
- glob-parent@5.1.2:
- dependencies:
- is-glob: 4.0.3
-
glob-parent@6.0.2:
dependencies:
is-glob: 4.0.3
globals@14.0.0: {}
- globalthis@1.0.4:
- dependencies:
- define-properties: 1.2.1
- gopd: 1.2.0
-
gopd@1.2.0: {}
graceful-fs@4.2.11: {}
- graphemer@1.4.0: {}
-
- has-bigints@1.1.0: {}
-
has-flag@4.0.0: {}
- has-property-descriptors@1.0.2:
- dependencies:
- es-define-property: 1.0.1
-
- has-proto@1.2.0:
- dependencies:
- dunder-proto: 1.0.1
-
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
@@ -4318,73 +2477,12 @@ snapshots:
inherits@2.0.4: {}
- internal-slot@1.1.0:
- dependencies:
- es-errors: 1.3.0
- hasown: 2.0.2
- side-channel: 1.1.0
-
- is-array-buffer@3.0.5:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- get-intrinsic: 1.3.0
-
is-arrayish@0.3.2: {}
- is-async-function@2.1.1:
- dependencies:
- async-function: 1.0.0
- call-bound: 1.0.4
- get-proto: 1.0.1
- has-tostringtag: 1.0.2
- safe-regex-test: 1.1.0
-
- is-bigint@1.1.0:
- dependencies:
- has-bigints: 1.1.0
-
- is-boolean-object@1.2.2:
- dependencies:
- call-bound: 1.0.4
- has-tostringtag: 1.0.2
-
- is-bun-module@2.0.0:
- dependencies:
- semver: 7.7.1
-
- is-callable@1.2.7: {}
-
- is-core-module@2.16.1:
- dependencies:
- hasown: 2.0.2
-
- is-data-view@1.0.2:
- dependencies:
- call-bound: 1.0.4
- get-intrinsic: 1.3.0
- is-typed-array: 1.1.15
-
- is-date-object@1.1.0:
- dependencies:
- call-bound: 1.0.4
- has-tostringtag: 1.0.2
-
is-docker@3.0.0: {}
is-extglob@2.1.1: {}
- is-finalizationregistry@1.1.1:
- dependencies:
- call-bound: 1.0.4
-
- is-generator-function@1.1.0:
- dependencies:
- call-bound: 1.0.4
- get-proto: 1.0.1
- has-tostringtag: 1.0.2
- safe-regex-test: 1.1.0
-
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
@@ -4393,81 +2491,22 @@ snapshots:
dependencies:
is-docker: 3.0.0
- is-map@2.0.3: {}
-
- is-number-object@1.1.1:
- dependencies:
- call-bound: 1.0.4
- has-tostringtag: 1.0.2
-
- is-number@7.0.0: {}
-
is-plain-obj@4.1.0: {}
- is-regex@1.2.1:
- dependencies:
- call-bound: 1.0.4
- gopd: 1.2.0
- has-tostringtag: 1.0.2
- hasown: 2.0.2
-
- is-set@2.0.3: {}
-
- is-shared-array-buffer@1.0.4:
- dependencies:
- call-bound: 1.0.4
-
is-stream@2.0.1: {}
is-stream@4.0.1: {}
- is-string@1.1.1:
- dependencies:
- call-bound: 1.0.4
- has-tostringtag: 1.0.2
-
- is-symbol@1.1.1:
- dependencies:
- call-bound: 1.0.4
- has-symbols: 1.1.0
- safe-regex-test: 1.1.0
-
- is-typed-array@1.1.15:
- dependencies:
- which-typed-array: 1.1.19
-
is-unicode-supported@2.1.0: {}
- is-weakmap@2.0.2: {}
-
- is-weakref@1.1.1:
- dependencies:
- call-bound: 1.0.4
-
- is-weakset@2.0.4:
- dependencies:
- call-bound: 1.0.4
- get-intrinsic: 1.3.0
-
is-what@4.1.16: {}
is-wsl@3.1.0:
dependencies:
is-inside-container: 1.0.0
- isarray@2.0.5: {}
-
isexe@2.0.0: {}
- iterator.prototype@1.1.5:
- dependencies:
- define-data-property: 1.1.4
- es-object-atoms: 1.1.1
- get-intrinsic: 1.3.0
- get-proto: 1.0.1
- has-symbols: 1.1.0
- set-function-name: 2.0.2
-
jiti@2.4.2: {}
js-tiktoken@1.0.20:
@@ -4486,34 +2525,23 @@ snapshots:
json-stable-stringify-without-jsonify@1.0.1: {}
- json5@1.0.2:
- dependencies:
- minimist: 1.2.8
-
jsonpointer@5.0.1: {}
- jsx-ast-utils@3.3.5:
- dependencies:
- array-includes: 3.1.8
- array.prototype.flat: 1.3.3
- object.assign: 4.1.7
- object.values: 1.2.1
-
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
kuler@2.0.0: {}
- langchain@0.3.24(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))(openai@4.96.2(zod@3.24.3)):
+ langchain@0.3.24(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))(openai@4.96.2(zod@3.24.3)):
dependencies:
- '@langchain/core': 0.3.49(openai@4.96.2(zod@3.24.3))
- '@langchain/openai': 0.5.7(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))
- '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.49(openai@4.96.2(zod@3.24.3)))
+ '@langchain/core': 0.3.50(openai@4.96.2(zod@3.24.3))
+ '@langchain/openai': 0.5.8(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))
+ '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.50(openai@4.96.2(zod@3.24.3)))
js-tiktoken: 1.0.20
js-yaml: 4.1.0
jsonpointer: 5.0.1
- langsmith: 0.3.22(openai@4.96.2(zod@3.24.3))
+ langsmith: 0.3.24(openai@4.96.2(zod@3.24.3))
openapi-types: 12.1.3
p-retry: 4.6.2
uuid: 10.0.0
@@ -4536,7 +2564,7 @@ snapshots:
optionalDependencies:
openai: 4.96.2(zod@3.24.3)
- langsmith@0.3.22(openai@4.96.2(zod@3.24.3)):
+ langsmith@0.3.24(openai@4.96.2(zod@3.24.3)):
dependencies:
'@types/uuid': 10.0.0
chalk: 4.1.2
@@ -4548,12 +2576,6 @@ snapshots:
optionalDependencies:
openai: 4.96.2(zod@3.24.3)
- language-subtag-registry@0.3.23: {}
-
- language-tags@1.0.9:
- dependencies:
- language-subtag-registry: 0.3.23
-
levn@0.4.1:
dependencies:
prelude-ls: 1.2.1
@@ -4623,23 +2645,8 @@ snapshots:
safe-stable-stringify: 2.5.0
triple-beam: 1.4.1
- loose-envify@1.4.0:
- dependencies:
- js-tokens: 4.0.0
-
- lucide-react@0.503.0(react@19.1.0):
- dependencies:
- react: 19.1.0
-
math-intrinsics@1.1.0: {}
- merge2@1.4.1: {}
-
- micromatch@4.0.8:
- dependencies:
- braces: 3.0.3
- picomatch: 2.3.1
-
mime-db@1.52.0: {}
mime-types@2.1.35:
@@ -4650,12 +2657,6 @@ snapshots:
dependencies:
brace-expansion: 1.1.11
- minimatch@9.0.5:
- dependencies:
- brace-expansion: 2.0.1
-
- minimist@1.2.8: {}
-
minipass@7.1.2: {}
minizlib@3.0.2:
@@ -4670,35 +2671,8 @@ snapshots:
nanoid@3.3.11: {}
- napi-postinstall@0.2.3: {}
-
natural-compare@1.4.0: {}
- next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
- dependencies:
- '@next/env': 15.3.1
- '@swc/counter': 0.1.3
- '@swc/helpers': 0.5.15
- busboy: 1.6.0
- caniuse-lite: 1.0.30001715
- postcss: 8.4.31
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
- styled-jsx: 5.1.6(react@19.1.0)
- optionalDependencies:
- '@next/swc-darwin-arm64': 15.3.1
- '@next/swc-darwin-x64': 15.3.1
- '@next/swc-linux-arm64-gnu': 15.3.1
- '@next/swc-linux-arm64-musl': 15.3.1
- '@next/swc-linux-x64-gnu': 15.3.1
- '@next/swc-linux-x64-musl': 15.3.1
- '@next/swc-win32-arm64-msvc': 15.3.1
- '@next/swc-win32-x64-msvc': 15.3.1
- sharp: 0.34.1
- transitivePeerDependencies:
- - '@babel/core'
- - babel-plugin-macros
-
node-domexception@1.0.0: {}
node-fetch@2.7.0:
@@ -4714,48 +2688,6 @@ snapshots:
path-key: 4.0.0
unicorn-magic: 0.3.0
- object-assign@4.1.1: {}
-
- object-inspect@1.13.4: {}
-
- object-keys@1.1.1: {}
-
- object.assign@4.1.7:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-object-atoms: 1.1.1
- has-symbols: 1.1.0
- object-keys: 1.1.1
-
- object.entries@1.1.9:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-object-atoms: 1.1.1
-
- object.fromentries@2.0.8:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-object-atoms: 1.1.1
-
- object.groupby@1.0.3:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
-
- object.values@1.2.1:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-object-atoms: 1.1.1
-
once@1.4.0:
dependencies:
wrappy: 1.0.2
@@ -4796,12 +2728,6 @@ snapshots:
type-check: 0.4.0
word-wrap: 1.2.5
- own-keys@1.0.1:
- dependencies:
- get-intrinsic: 1.3.0
- object-keys: 1.1.1
- safe-push-apply: 1.0.0
-
p-finally@1.0.0: {}
p-limit@3.1.0:
@@ -4838,18 +2764,10 @@ snapshots:
path-key@4.0.0: {}
- path-parse@1.0.7: {}
-
pend@1.2.0: {}
picocolors@1.1.1: {}
- picomatch@2.3.1: {}
-
- picomatch@4.0.2: {}
-
- possible-typed-array-names@1.1.0: {}
-
postcss-modules-extract-imports@3.1.0(postcss@8.5.3):
dependencies:
postcss: 8.5.3
@@ -4890,12 +2808,6 @@ snapshots:
postcss-value-parser@4.2.0: {}
- postcss@8.4.31:
- dependencies:
- nanoid: 3.3.11
- picocolors: 1.1.1
- source-map-js: 1.2.1
-
postcss@8.5.3:
dependencies:
nanoid: 3.3.11
@@ -4908,12 +2820,6 @@ snapshots:
dependencies:
parse-ms: 4.0.0
- prop-types@15.8.1:
- dependencies:
- loose-envify: 1.4.0
- object-assign: 4.1.1
- react-is: 16.13.1
-
pump@3.0.2:
dependencies:
end-of-stream: 1.4.4
@@ -4921,17 +2827,6 @@ snapshots:
punycode@2.3.1: {}
- queue-microtask@1.2.3: {}
-
- react-dom@19.1.0(react@19.1.0):
- dependencies:
- react: 19.1.0
- scheduler: 0.26.0
-
- react-is@16.13.1: {}
-
- react@19.1.0: {}
-
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
@@ -4940,165 +2835,26 @@ snapshots:
readdirp@4.1.2: {}
- reflect.getprototypeof@1.0.10:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-errors: 1.3.0
- es-object-atoms: 1.1.1
- get-intrinsic: 1.3.0
- get-proto: 1.0.1
- which-builtin-type: 1.2.1
-
- regexp.prototype.flags@1.5.4:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-errors: 1.3.0
- get-proto: 1.0.1
- gopd: 1.2.0
- set-function-name: 2.0.2
-
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
- resolve@1.22.10:
- dependencies:
- is-core-module: 2.16.1
- path-parse: 1.0.7
- supports-preserve-symlinks-flag: 1.0.0
-
- resolve@2.0.0-next.5:
- dependencies:
- is-core-module: 2.16.1
- path-parse: 1.0.7
- supports-preserve-symlinks-flag: 1.0.0
-
retry@0.13.1: {}
- reusify@1.1.0: {}
-
run-applescript@7.0.0: {}
- run-parallel@1.2.0:
- dependencies:
- queue-microtask: 1.2.3
-
- safe-array-concat@1.1.3:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- get-intrinsic: 1.3.0
- has-symbols: 1.1.0
- isarray: 2.0.5
-
safe-buffer@5.2.1: {}
- safe-push-apply@1.0.0:
- dependencies:
- es-errors: 1.3.0
- isarray: 2.0.5
-
- safe-regex-test@1.1.0:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-regex: 1.2.1
-
safe-stable-stringify@2.5.0: {}
- scheduler@0.26.0: {}
-
- semver@6.3.1: {}
-
semver@7.7.1: {}
- set-function-length@1.2.2:
- dependencies:
- define-data-property: 1.1.4
- es-errors: 1.3.0
- function-bind: 1.1.2
- get-intrinsic: 1.3.0
- gopd: 1.2.0
- has-property-descriptors: 1.0.2
-
- set-function-name@2.0.2:
- dependencies:
- define-data-property: 1.1.4
- es-errors: 1.3.0
- functions-have-names: 1.2.3
- has-property-descriptors: 1.0.2
-
- set-proto@1.0.0:
- dependencies:
- dunder-proto: 1.0.1
- es-errors: 1.3.0
- es-object-atoms: 1.1.1
-
- sharp@0.34.1:
- dependencies:
- color: 4.2.3
- detect-libc: 2.0.4
- semver: 7.7.1
- optionalDependencies:
- '@img/sharp-darwin-arm64': 0.34.1
- '@img/sharp-darwin-x64': 0.34.1
- '@img/sharp-libvips-darwin-arm64': 1.1.0
- '@img/sharp-libvips-darwin-x64': 1.1.0
- '@img/sharp-libvips-linux-arm': 1.1.0
- '@img/sharp-libvips-linux-arm64': 1.1.0
- '@img/sharp-libvips-linux-ppc64': 1.1.0
- '@img/sharp-libvips-linux-s390x': 1.1.0
- '@img/sharp-libvips-linux-x64': 1.1.0
- '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
- '@img/sharp-libvips-linuxmusl-x64': 1.1.0
- '@img/sharp-linux-arm': 0.34.1
- '@img/sharp-linux-arm64': 0.34.1
- '@img/sharp-linux-s390x': 0.34.1
- '@img/sharp-linux-x64': 0.34.1
- '@img/sharp-linuxmusl-arm64': 0.34.1
- '@img/sharp-linuxmusl-x64': 0.34.1
- '@img/sharp-wasm32': 0.34.1
- '@img/sharp-win32-ia32': 0.34.1
- '@img/sharp-win32-x64': 0.34.1
- optional: true
-
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
shebang-regex@3.0.0: {}
- side-channel-list@1.0.0:
- dependencies:
- es-errors: 1.3.0
- object-inspect: 1.13.4
-
- side-channel-map@1.0.1:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- get-intrinsic: 1.3.0
- object-inspect: 1.13.4
-
- side-channel-weakmap@1.0.2:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- get-intrinsic: 1.3.0
- object-inspect: 1.13.4
- side-channel-map: 1.0.1
-
- side-channel@1.1.0:
- dependencies:
- es-errors: 1.3.0
- object-inspect: 1.13.4
- side-channel-list: 1.0.0
- side-channel-map: 1.0.1
- side-channel-weakmap: 1.0.2
-
signal-exit@4.1.0: {}
simple-swizzle@0.2.2:
@@ -5109,83 +2865,22 @@ snapshots:
source-map-js@1.2.1: {}
- stable-hash@0.0.5: {}
-
stack-trace@0.0.10: {}
stacktrace-parser@0.1.11:
dependencies:
type-fest: 0.7.1
- streamsearch@1.1.0: {}
-
string-hash@1.1.3: {}
- string.prototype.includes@2.0.1:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-abstract: 1.23.9
-
- string.prototype.matchall@4.0.12:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-errors: 1.3.0
- es-object-atoms: 1.1.1
- get-intrinsic: 1.3.0
- gopd: 1.2.0
- has-symbols: 1.1.0
- internal-slot: 1.1.0
- regexp.prototype.flags: 1.5.4
- set-function-name: 2.0.2
- side-channel: 1.1.0
-
- string.prototype.repeat@1.0.0:
- dependencies:
- define-properties: 1.2.1
- es-abstract: 1.23.9
-
- string.prototype.trim@1.2.10:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-data-property: 1.1.4
- define-properties: 1.2.1
- es-abstract: 1.23.9
- es-object-atoms: 1.1.1
- has-property-descriptors: 1.0.2
-
- string.prototype.trimend@1.0.9:
- dependencies:
- call-bind: 1.0.8
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-object-atoms: 1.1.1
-
- string.prototype.trimstart@1.0.8:
- dependencies:
- call-bind: 1.0.8
- define-properties: 1.2.1
- es-object-atoms: 1.1.1
-
string_decoder@1.3.0:
dependencies:
safe-buffer: 5.2.1
- strip-bom@3.0.0: {}
-
strip-final-newline@4.0.0: {}
strip-json-comments@3.1.1: {}
- styled-jsx@5.1.6(react@19.1.0):
- dependencies:
- client-only: 0.0.1
- react: 19.1.0
-
superjson@2.2.2:
dependencies:
copy-anything: 3.0.5
@@ -5194,11 +2889,7 @@ snapshots:
dependencies:
has-flag: 4.0.0
- supports-preserve-symlinks-flag@1.0.0: {}
-
- tailwind-merge@3.2.0: {}
-
- tailwindcss@4.1.4: {}
+ tailwindcss@4.1.5: {}
tapable@2.2.1: {}
@@ -5213,32 +2904,10 @@ snapshots:
text-hex@1.0.0: {}
- tinyglobby@0.2.13:
- dependencies:
- fdir: 6.4.4(picomatch@4.0.2)
- picomatch: 4.0.2
-
- to-regex-range@5.0.1:
- dependencies:
- is-number: 7.0.0
-
tr46@0.0.3: {}
triple-beam@1.4.1: {}
- ts-api-utils@2.1.0(typescript@5.8.3):
- dependencies:
- typescript: 5.8.3
-
- tsconfig-paths@3.15.0:
- dependencies:
- '@types/json5': 0.0.29
- json5: 1.0.2
- minimist: 1.2.8
- strip-bom: 3.0.0
-
- tslib@2.8.1: {}
-
tsx@4.19.4:
dependencies:
esbuild: 0.25.3
@@ -5246,84 +2915,20 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
- tw-animate-css@1.2.8: {}
-
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
type-fest@0.7.1: {}
- typed-array-buffer@1.0.3:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-typed-array: 1.1.15
-
- typed-array-byte-length@1.0.3:
- dependencies:
- call-bind: 1.0.8
- for-each: 0.3.5
- gopd: 1.2.0
- has-proto: 1.2.0
- is-typed-array: 1.1.15
-
- typed-array-byte-offset@1.0.4:
- dependencies:
- available-typed-arrays: 1.0.7
- call-bind: 1.0.8
- for-each: 0.3.5
- gopd: 1.2.0
- has-proto: 1.2.0
- is-typed-array: 1.1.15
- reflect.getprototypeof: 1.0.10
-
- typed-array-length@1.0.7:
- dependencies:
- call-bind: 1.0.8
- for-each: 0.3.5
- gopd: 1.2.0
- is-typed-array: 1.1.15
- possible-typed-array-names: 1.1.0
- reflect.getprototypeof: 1.0.10
-
typescript@5.8.3: {}
- unbox-primitive@1.1.0:
- dependencies:
- call-bound: 1.0.4
- has-bigints: 1.1.0
- has-symbols: 1.1.0
- which-boxed-primitive: 1.1.1
-
undici-types@5.26.5: {}
undici-types@6.19.8: {}
unicorn-magic@0.3.0: {}
- unrs-resolver@1.7.2:
- dependencies:
- napi-postinstall: 0.2.3
- optionalDependencies:
- '@unrs/resolver-binding-darwin-arm64': 1.7.2
- '@unrs/resolver-binding-darwin-x64': 1.7.2
- '@unrs/resolver-binding-freebsd-x64': 1.7.2
- '@unrs/resolver-binding-linux-arm-gnueabihf': 1.7.2
- '@unrs/resolver-binding-linux-arm-musleabihf': 1.7.2
- '@unrs/resolver-binding-linux-arm64-gnu': 1.7.2
- '@unrs/resolver-binding-linux-arm64-musl': 1.7.2
- '@unrs/resolver-binding-linux-ppc64-gnu': 1.7.2
- '@unrs/resolver-binding-linux-riscv64-gnu': 1.7.2
- '@unrs/resolver-binding-linux-riscv64-musl': 1.7.2
- '@unrs/resolver-binding-linux-s390x-gnu': 1.7.2
- '@unrs/resolver-binding-linux-x64-gnu': 1.7.2
- '@unrs/resolver-binding-linux-x64-musl': 1.7.2
- '@unrs/resolver-binding-wasm32-wasi': 1.7.2
- '@unrs/resolver-binding-win32-arm64-msvc': 1.7.2
- '@unrs/resolver-binding-win32-ia32-msvc': 1.7.2
- '@unrs/resolver-binding-win32-x64-msvc': 1.7.2
-
update-browserslist-db@1.1.3(browserslist@4.24.4):
dependencies:
browserslist: 4.24.4
@@ -5349,47 +2954,6 @@ snapshots:
tr46: 0.0.3
webidl-conversions: 3.0.1
- which-boxed-primitive@1.1.1:
- dependencies:
- is-bigint: 1.1.0
- is-boolean-object: 1.2.2
- is-number-object: 1.1.1
- is-string: 1.1.1
- is-symbol: 1.1.1
-
- which-builtin-type@1.2.1:
- dependencies:
- call-bound: 1.0.4
- function.prototype.name: 1.1.8
- has-tostringtag: 1.0.2
- is-async-function: 2.1.1
- is-date-object: 1.1.0
- is-finalizationregistry: 1.1.1
- is-generator-function: 1.1.0
- is-regex: 1.2.1
- is-weakref: 1.1.1
- isarray: 2.0.5
- which-boxed-primitive: 1.1.1
- which-collection: 1.0.2
- which-typed-array: 1.1.19
-
- which-collection@1.0.2:
- dependencies:
- is-map: 2.0.3
- is-set: 2.0.3
- is-weakmap: 2.0.2
- is-weakset: 2.0.4
-
- which-typed-array@1.1.19:
- dependencies:
- available-typed-arrays: 1.0.7
- call-bind: 1.0.8
- call-bound: 1.0.4
- for-each: 0.3.5
- get-proto: 1.0.1
- gopd: 1.2.0
- has-tostringtag: 1.0.2
-
which@2.0.2:
dependencies:
isexe: 2.0.0
diff --git a/postcss.config.mjs b/postcss.config.mjs
deleted file mode 100644
index c7bcb4b..0000000
--- a/postcss.config.mjs
+++ /dev/null
@@ -1,5 +0,0 @@
-const config = {
- plugins: ["@tailwindcss/postcss"],
-};
-
-export default config;
diff --git a/public/file.svg b/public/file.svg
deleted file mode 100644
index 004145c..0000000
--- a/public/file.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/globe.svg b/public/globe.svg
deleted file mode 100644
index 567f17b..0000000
--- a/public/globe.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/next.svg b/public/next.svg
deleted file mode 100644
index 5174b28..0000000
--- a/public/next.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/vercel.svg b/public/vercel.svg
deleted file mode 100644
index 7705396..0000000
--- a/public/vercel.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/window.svg b/public/window.svg
deleted file mode 100644
index b2b2a44..0000000
--- a/public/window.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/python/configuration.py b/python/configuration.py
deleted file mode 100644
index ffa0dea..0000000
--- a/python/configuration.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""Define the configurable parameters for the agent."""
-
-import os
-from dataclasses import dataclass, fields
-from typing import Any, Optional
-
-from langchain_core.runnables import RunnableConfig
-
-@dataclass(kw_only=True)
-class Configuration:
- """Main configuration class."""
- # llm =
-
- @classmethod
- def from_runnable_config(
- cls, config: Optional[RunnableConfig] = None
- ) -> "Configuration":
- """Create a Configuration instance from a RunnableConfig."""
- configurable = (
- config["configurable"] if config and "configurable" in config else {}
- )
- values: dict[str, Any] = {
- f.name: os.environ.get(f.name.upper(), configurable.get(f.name))
- for f in fields(cls)
- if f.init
- }
-
- return cls(**{k: v for k, v in values.items() if v})
\ No newline at end of file
diff --git a/python/email_assistant.py b/python/email_assistant.py
deleted file mode 100644
index 1a7242a..0000000
--- a/python/email_assistant.py
+++ /dev/null
@@ -1,159 +0,0 @@
-from typing import Literal
-
-from langchain.chat_models import init_chat_model
-
-from src.email_assistant.tools import get_tools, get_tools_by_name
-from src.email_assistant.tools.default.prompt_templates import AGENT_TOOLS_PROMPT
-from src.email_assistant.prompts import triage_system_prompt, triage_user_prompt, agent_system_prompt, default_background, default_triage_instructions, default_response_preferences, default_cal_preferences
-from src.email_assistant.schemas import State, RouterSchema, StateInput
-from src.email_assistant.utils import parse_email, format_email_markdown
-
-from langgraph.graph import StateGraph, START, END
-from langgraph.types import Command
-
-# Get tools
-tools = get_tools()
-tools_by_name = get_tools_by_name(tools)
-
-# Initialize the LLM for use with router / structured output
-llm = init_chat_model("openai:gpt-4.1", temperature=0.0)
-llm_router = llm.with_structured_output(RouterSchema)
-
-# Initialize the LLM, enforcing tool use (of any available tools) for agent
-llm = init_chat_model("openai:gpt-4.1", temperature=0.0)
-llm_with_tools = llm.bind_tools(tools, tool_choice="required")
-
-# Nodes
-def llm_call(state: State):
- """LLM decides whether to call a tool or not"""
-
- return {
- "messages": [
- llm_with_tools.invoke(
- [
- {"role": "system", "content": agent_system_prompt.format(
- tools_prompt=AGENT_TOOLS_PROMPT,
- background=default_background,
- response_preferences=default_response_preferences,
- cal_preferences=default_cal_preferences)
- },
-
- ]
- + state["messages"]
- )
- ]
- }
-
-def tool_node(state: dict):
- """Performs the tool call"""
-
- result = []
- for tool_call in state["messages"][-1].tool_calls:
- tool = tools_by_name[tool_call["name"]]
- observation = tool.invoke(tool_call["args"])
- result.append({"role": "tool", "content" : observation, "tool_call_id": tool_call["id"]})
- return {"messages": result}
-
-# Conditional edge function
-def should_continue(state: State) -> Literal["Action", END]:
- """Route to Action, or end if Done tool called"""
- messages = state["messages"]
- last_message = messages[-1]
- if last_message.tool_calls:
- for tool_call in last_message.tool_calls:
- if tool_call["name"] == "Done":
- return END
- else:
- return "Action"
-
-# Build workflow
-agent_builder = StateGraph(State)
-
-# Add nodes
-agent_builder.add_node("llm_call", llm_call)
-agent_builder.add_node("environment", tool_node)
-
-# Add edges to connect nodes
-agent_builder.add_edge(START, "llm_call")
-agent_builder.add_conditional_edges(
- "llm_call",
- should_continue,
- {
- # Name returned by should_continue : Name of next node to visit
- "Action": "environment",
- END: END,
- },
-)
-agent_builder.add_edge("environment", "llm_call")
-
-# Compile the agent
-agent = agent_builder.compile()
-
-def triage_router(state: State) -> Command[Literal["response_agent", "__end__"]]:
- """Analyze email content to decide if we should respond, notify, or ignore.
-
- The triage step prevents the assistant from wasting time on:
- - Marketing emails and spam
- - Company-wide announcements
- - Messages meant for other teams
- """
- author, to, subject, email_thread = parse_email(state["email_input"])
- system_prompt = triage_system_prompt.format(
- background=default_background,
- triage_instructions=default_triage_instructions
- )
-
- user_prompt = triage_user_prompt.format(
- author=author, to=to, subject=subject, email_thread=email_thread
- )
-
- # Create email markdown for Agent Inbox in case of notification
- email_markdown = format_email_markdown(subject, author, to, email_thread)
-
- # Run the router LLM
- result = llm_router.invoke(
- [
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_prompt},
- ]
- )
-
- # Decision
- classification = result.classification
-
- if classification == "respond":
- print("📧 Classification: RESPOND - This email requires a response")
- goto = "response_agent"
- # Add the email to the messages
- update = {
- "classification_decision": result.classification,
- "messages": [{"role": "user",
- "content": f"Respond to the email: {email_markdown}"
- }],
- }
- elif result.classification == "ignore":
- print("🚫 Classification: IGNORE - This email can be safely ignored")
- update = {
- "classification_decision": result.classification,
- }
- goto = END
- elif result.classification == "notify":
- # If real life, this would do something else
- print("🔔 Classification: NOTIFY - This email contains important information")
- update = {
- "classification_decision": result.classification,
- }
- goto = END
- else:
- raise ValueError(f"Invalid classification: {result.classification}")
- return Command(goto=goto, update=update)
-
-# Build workflow
-overall_workflow = (
- StateGraph(State, input=StateInput)
- .add_node(triage_router)
- .add_node("response_agent", agent)
- .add_edge(START, "triage_router")
-)
-
-email_assistant = overall_workflow.compile()
\ No newline at end of file
diff --git a/python/email_assistant_hitl.py b/python/email_assistant_hitl.py
deleted file mode 100644
index b74b18b..0000000
--- a/python/email_assistant_hitl.py
+++ /dev/null
@@ -1,391 +0,0 @@
-from typing import Literal
-
-from langchain.chat_models import init_chat_model
-
-from langgraph.graph import StateGraph, START, END
-from langgraph.types import interrupt, Command
-
-from src.email_assistant.tools import get_tools, get_tools_by_name
-from src.email_assistant.tools.default.prompt_templates import HITL_TOOLS_PROMPT
-from src.email_assistant.prompts import triage_system_prompt, triage_user_prompt, agent_system_prompt_hitl, default_background, default_triage_instructions, default_response_preferences, default_cal_preferences
-from src.email_assistant.schemas import State, RouterSchema, StateInput
-from src.email_assistant.utils import parse_email, format_for_display, format_email_markdown
-
-# Get tools
-tools = get_tools(["write_email", "schedule_meeting", "check_calendar_availability", "Question", "Done"])
-tools_by_name = get_tools_by_name(tools)
-
-# Initialize the LLM for use with router / structured output
-llm = init_chat_model("openai:gpt-4.1", temperature=0.0)
-llm_router = llm.with_structured_output(RouterSchema)
-
-# Initialize the LLM, enforcing tool use (of any available tools) for agent
-llm = init_chat_model("openai:gpt-4.1", temperature=0.0)
-llm_with_tools = llm.bind_tools(tools, tool_choice="required")
-
-# Nodes
-def triage_router(state: State) -> Command[Literal["triage_interrupt_handler", "response_agent", "__end__"]]:
- """Analyze email content to decide if we should respond, notify, or ignore.
-
- The triage step prevents the assistant from wasting time on:
- - Marketing emails and spam
- - Company-wide announcements
- - Messages meant for other teams
- """
-
- # Parse the email input
- author, to, subject, email_thread = parse_email(state["email_input"])
- user_prompt = triage_user_prompt.format(
- author=author, to=to, subject=subject, email_thread=email_thread
- )
-
- # Create email markdown for Agent Inbox in case of notification
- email_markdown = format_email_markdown(subject, author, to, email_thread)
-
- # Format system prompt with background and triage instructions
- system_prompt = triage_system_prompt.format(
- background=default_background,
- triage_instructions=default_triage_instructions
- )
-
- # Run the router LLM
- result = llm_router.invoke(
- [
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_prompt},
- ]
- )
-
- # Decision
- classification = result.classification
-
- # Process the classification decision
- if classification == "respond":
- print("📧 Classification: RESPOND - This email requires a response")
- # Next node
- goto = "response_agent"
- # Update the state
- update = {
- "classification_decision": result.classification,
- "messages": [{"role": "user",
- "content": f"Respond to the email: {email_markdown}"
- }],
- }
- elif classification == "ignore":
- print("🚫 Classification: IGNORE - This email can be safely ignored")
-
- # Next node
- goto = END
- # Update the state
- update = {
- "classification_decision": classification,
- }
-
- elif classification == "notify":
- print("🔔 Classification: NOTIFY - This email contains important information")
-
- # Next node
- goto = "triage_interrupt_handler"
- # Update the state
- update = {
- "classification_decision": classification,
- }
-
- else:
- raise ValueError(f"Invalid classification: {classification}")
- return Command(goto=goto, update=update)
-
-def triage_interrupt_handler(state: State) -> Command[Literal["response_agent", "__end__"]]:
- """Handles interrupts from the triage step"""
-
- # Parse the email input
- author, to, subject, email_thread = parse_email(state["email_input"])
-
- # Create email markdown for Agent Inbox in case of notification
- email_markdown = format_email_markdown(subject, author, to, email_thread)
-
- # Create messages
- messages = [{"role": "user",
- "content": f"Email to notify user about: {email_markdown}"
- }]
-
- # Create interrupt for Agent Inbox
- request = {
- "action_request": {
- "action": f"Email Assistant: {state['classification_decision']}",
- "args": {}
- },
- "config": {
- "allow_ignore": True,
- "allow_respond": True,
- "allow_edit": False,
- "allow_accept": False,
- },
- # Email to show in Agent Inbox
- "description": email_markdown,
- }
-
- # Agent Inbox responds with a list
- response = interrupt([request])[0]
-
- # If user provides feedback, go to response agent and use feedback to respond to email
- if response["type"] == "response":
- # Add feedback to messages
- user_input = response["args"]
- # Used by the response agent
- messages.append({"role": "user",
- "content": f"User wants to reply to the email. Use this feedback to respond: {user_input}"
- })
- # Go to response agent
- goto = "response_agent"
-
- # If user ignores email, go to END
- elif response["type"] == "ignore":
- goto = END
-
- # Catch all other responses
- else:
- raise ValueError(f"Invalid response: {response}")
-
- # Update the state
- update = {
- "messages": messages,
- }
-
- return Command(goto=goto, update=update)
-
-def llm_call(state: State):
- """LLM decides whether to call a tool or not"""
-
- return {
- "messages": [
- llm_with_tools.invoke(
- [
- {"role": "system", "content": agent_system_prompt_hitl.format(
- tools_prompt=HITL_TOOLS_PROMPT,
- background=default_background,
- response_preferences=default_response_preferences,
- cal_preferences=default_cal_preferences
- )}
- ]
- + state["messages"]
- )
- ]
- }
-
-def interrupt_handler(state: State) -> Command[Literal["llm_call", "__end__"]]:
- """Creates an interrupt for human review of tool calls"""
-
- # Store messages
- result = []
-
- # Go to the LLM call node next
- goto = "llm_call"
-
- # Iterate over the tool calls in the last message
- for tool_call in state["messages"][-1].tool_calls:
-
- # Allowed tools for HITL
- hitl_tools = ["write_email", "schedule_meeting", "Question"]
-
- # If tool is not in our HITL list, execute it directly without interruption
- if tool_call["name"] not in hitl_tools:
-
- # Execute search_memory and other tools without interruption
- tool = tools_by_name[tool_call["name"]]
- observation = tool.invoke(tool_call["args"])
- result.append({"role": "tool", "content": observation, "tool_call_id": tool_call["id"]})
- continue
-
- # Get original email from email_input in state
- email_input = state["email_input"]
- author, to, subject, email_thread = parse_email(email_input)
- original_email_markdown = format_email_markdown(subject, author, to, email_thread)
-
- # Format tool call for display and prepend the original email
- tool_display = format_for_display(state, tool_call)
- description = original_email_markdown + tool_display
-
- # Configure what actions are allowed in Agent Inbox
- if tool_call["name"] == "write_email":
- config = {
- "allow_ignore": True,
- "allow_respond": True,
- "allow_edit": True,
- "allow_accept": True,
- }
- elif tool_call["name"] == "schedule_meeting":
- config = {
- "allow_ignore": True,
- "allow_respond": True,
- "allow_edit": True,
- "allow_accept": True,
- }
- elif tool_call["name"] == "Question":
- config = {
- "allow_ignore": True,
- "allow_respond": True,
- "allow_edit": False,
- "allow_accept": False,
- }
- else:
- raise ValueError(f"Invalid tool call: {tool_call['name']}")
-
- # Create the interrupt request
- request = {
- "action_request": {
- "action": tool_call["name"],
- "args": tool_call["args"]
- },
- "config": config,
- "description": description,
- }
-
- # Send to Agent Inbox and wait for response
- response = interrupt([request])[0]
-
- # Handle the responses
- if response["type"] == "accept":
-
- # Execute the tool with original args
- tool = tools_by_name[tool_call["name"]]
- observation = tool.invoke(tool_call["args"])
- result.append({"role": "tool", "content": observation, "tool_call_id": tool_call["id"]})
-
- elif response["type"] == "edit":
-
- # Tool selection
- tool = tools_by_name[tool_call["name"]]
-
- # Get edited args from Agent Inbox
- edited_args = response["args"]["args"]
-
- # Update the write_email tool call with the edited content from Agent Inbox
- if tool_call["name"] == "write_email":
-
- # Update the AI message's tool call with edited content (reference to the message in the state)
- ai_message = state["messages"][-1]
- current_id = tool_call["id"]
-
- # Replace the original tool call with the edited one (any changes made to this reference affect the original object in the state)
- ai_message.tool_calls = [tc for tc in ai_message.tool_calls if tc["id"] != current_id] + [
- {"type": "tool_call", "name": tool_call["name"], "args": edited_args, "id": current_id}
- ]
-
- # Execute the tool with edited args
- observation = tool.invoke(edited_args)
-
- # Add only the tool response message
- result.append({"role": "tool", "content": observation, "tool_call_id": current_id})
-
- # Update the schedule_meeting tool call with the edited content from Agent Inbox
- elif tool_call["name"] == "schedule_meeting":
-
- # Update the AI message's tool call with edited content
- ai_message = state["messages"][-1]
- current_id = tool_call["id"]
-
- # Replace the original tool call with the edited one
- ai_message.tool_calls = [tc for tc in ai_message.tool_calls if tc["id"] != current_id] + [
- {"type": "tool_call", "name": tool_call["name"], "args": edited_args, "id": current_id}
- ]
-
- # Execute the tool with edited args
- observation = tool.invoke(edited_args)
-
- # Add only the tool response message
- result.append({"role": "tool", "content": observation, "tool_call_id": current_id})
-
- # Catch all other tool calls
- else:
- raise ValueError(f"Invalid tool call: {tool_call['name']}")
-
- elif response["type"] == "ignore":
- if tool_call["name"] == "write_email":
- # Don't execute the tool, and tell the agent how to proceed
- result.append({"role": "tool", "content": "User ignored this email draft. Ignore this email and end the workflow.", "tool_call_id": tool_call["id"]})
- # Go to END
- goto = END
- elif tool_call["name"] == "schedule_meeting":
- # Don't execute the tool, and tell the agent how to proceed
- result.append({"role": "tool", "content": "User ignored this calendar meeting draft. Ignore this email and end the workflow.", "tool_call_id": tool_call["id"]})
- # Go to END
- goto = END
- elif tool_call["name"] == "Question":
- # Don't execute the tool, and tell the agent how to proceed
- result.append({"role": "tool", "content": "User ignored this question. Ignore this email and end the workflow.", "tool_call_id": tool_call["id"]})
- # Go to END
- goto = END
- else:
- raise ValueError(f"Invalid tool call: {tool_call['name']}")
-
- elif response["type"] == "response":
- # User provided feedback
- user_feedback = response["args"]
- if tool_call["name"] == "write_email":
- # Don't execute the tool, and add a message with the user feedback to incorporate into the email
- result.append({"role": "tool", "content": f"User gave feedback, which can we incorporate into the email. Feedback: {user_feedback}", "tool_call_id": tool_call["id"]})
- elif tool_call["name"] == "schedule_meeting":
- # Don't execute the tool, and add a message with the user feedback to incorporate into the email
- result.append({"role": "tool", "content": f"User gave feedback, which can we incorporate into the meeting request. Feedback: {user_feedback}", "tool_call_id": tool_call["id"]})
- elif tool_call["name"] == "Question":
- # Don't execute the tool, and add a message with the user feedback to incorporate into the email
- result.append({"role": "tool", "content": f"User answered the question, which can we can use for any follow up actions. Feedback: {user_feedback}", "tool_call_id": tool_call["id"]})
- else:
- raise ValueError(f"Invalid tool call: {tool_call['name']}")
-
- # Catch all other responses
- else:
- raise ValueError(f"Invalid response: {response}")
-
- # Update the state
- update = {
- "messages": result,
- }
-
- return Command(goto=goto, update=update)
-
-# Conditional edge function
-def should_continue(state: State) -> Literal["interrupt_handler", END]:
- """Route to tool handler, or end if Done tool called"""
- messages = state["messages"]
- last_message = messages[-1]
- if last_message.tool_calls:
- for tool_call in last_message.tool_calls:
- if tool_call["name"] == "Done":
- return END
- else:
- return "interrupt_handler"
-
-# Build workflow
-agent_builder = StateGraph(State)
-
-# Add nodes
-agent_builder.add_node("llm_call", llm_call)
-agent_builder.add_node("interrupt_handler", interrupt_handler)
-
-# Add edges
-agent_builder.add_edge(START, "llm_call")
-agent_builder.add_conditional_edges(
- "llm_call",
- should_continue,
- {
- "interrupt_handler": "interrupt_handler",
- END: END,
- },
-)
-
-# Compile the agent
-response_agent = agent_builder.compile()
-
-# Build overall workflow
-overall_workflow = (
- StateGraph(State, input=StateInput)
- .add_node(triage_router)
- .add_node(triage_interrupt_handler)
- .add_node("response_agent", response_agent)
- .add_edge(START, "triage_router")
-
-)
-
-email_assistant = overall_workflow.compile()
\ No newline at end of file
diff --git a/python/email_assistant_hitl_memory.py b/python/email_assistant_hitl_memory.py
deleted file mode 100644
index 3e4bb58..0000000
--- a/python/email_assistant_hitl_memory.py
+++ /dev/null
@@ -1,580 +0,0 @@
-import os
-from typing import Literal
-from pydantic import BaseModel
-
-from langchain.chat_models import init_chat_model
-
-from langgraph.graph import StateGraph, START, END
-from langgraph.store.base import BaseStore
-from langgraph.types import interrupt, Command
-
-from src.email_assistant.tools import get_tools, get_tools_by_name
-from src.email_assistant.tools.default.prompt_templates import HITL_MEMORY_TOOLS_PROMPT
-from src.email_assistant.prompts import triage_system_prompt, triage_user_prompt, agent_system_prompt_hitl_memory, default_triage_instructions, default_background, default_response_preferences, default_cal_preferences
-from src.email_assistant.schemas import State, RouterSchema, StateInput
-from src.email_assistant.utils import parse_email, format_for_display, format_email_markdown
-
-# Get tools
-tools = get_tools(["write_email", "schedule_meeting", "check_calendar_availability", "Question", "Done"])
-tools_by_name = get_tools_by_name(tools)
-
-# Initialize the LLM for use with router / structured output
-llm = init_chat_model("openai:gpt-4.1", temperature=0.0)
-llm_router = llm.with_structured_output(RouterSchema)
-
-# Initialize the LLM, enforcing tool use (of any available tools) for agent
-llm = init_chat_model("openai:gpt-4.1", temperature=0.0)
-llm_with_tools = llm.bind_tools(tools, tool_choice="required")
-
-def get_memory(store, namespace, default_content=None):
- """Get memory from the store or initialize with default if it doesn't exist.
-
- Args:
- store: LangGraph BaseStore instance to search for existing memory
- namespace: Tuple defining the memory namespace, e.g. ("email_assistant", "triage_preferences")
- default_content: Default content to use if memory doesn't exist
-
- Returns:
- str: The content of the memory profile, either from existing memory or the default
- """
- # Search for existing memory with namespace and key
- user_preferences = store.get(namespace, "user_preferences")
-
- # If memory exists, return its content (the value)
- if user_preferences:
- return user_preferences.value
-
- # If memory doesn't exist, add it to the store and return the default content
- else:
- # Namespace, key, value
- store.put(namespace, "user_preferences", default_content)
- user_preferences = default_content
-
- # Return the default content
- return user_preferences
-
-class UserPreferences(BaseModel):
- """User preferences."""
- preferences: str
- justification: str
-
-MEMORY_UPDATE_INSTRUCTIONS = """
-# Role and Objective
-You are a memory profile manager for an email assistant agent that selectively updates user preferences based on feedback messages from human-in-the-loop interactions with the email assistant.
-
-# Instructions
-- NEVER overwrite the entire memory profile
-- ONLY make targeted additions of new information
-- ONLY update specific facts that are directly contradicted by feedback messages
-- PRESERVE all other existing information in the profile
-- Format the profile consistently with the original style
-- Generate the profile as a string
-
-# Reasoning Steps
-1. Analyze the current memory profile structure and content
-2. Review feedback messages from human-in-the-loop interactions
-3. Extract relevant user preferences from these feedback messages (such as edits to emails/calendar invites, explicit feedback on assistant performance, user decisions to ignore certain emails)
-4. Compare new information against existing profile
-5. Identify only specific facts to add or update
-6. Preserve all other existing information
-7. Output the complete updated profile
-
-# Example
-
-RESPOND:
-- wife
-- specific questions
-- system admin notifications
-NOTIFY:
-- meeting invites
-IGNORE:
-- marketing emails
-- company-wide announcements
-- messages meant for other teams
-
-
-
-"The assistant shouldn't have responded to that system admin notification."
-
-
-
-RESPOND:
-- wife
-- specific questions
-NOTIFY:
-- meeting invites
-- system admin notifications
-IGNORE:
-- marketing emails
-- company-wide announcements
-- messages meant for other teams
-
-
-# Process current profile for {namespace}
-
-{current_profile}
-
-
-Think step by step about what specific feedback is being provided and what specific information should be added or updated in the profile while preserving everything else."""
-
-MEMORY_UPDATE_INSTRUCTIONS_REINFORCEMENT = """
-Remember:
-- NEVER overwrite the entire profile
-- ONLY make targeted additions or changes based on explicit feedback
-- PRESERVE all existing information not directly contradicted
-- Output the complete updated profile as a string
-"""
-
-def update_memory(store, namespace, messages):
- """Update memory profile in the store.
-
- Args:
- store: LangGraph BaseStore instance to update memory
- namespace: Tuple defining the memory namespace, e.g. ("email_assistant", "triage_preferences")
- messages: List of messages to update the memory with
- """
-
- # Get the existing memory
- user_preferences = store.get(namespace, "user_preferences")
- # Update the memory
- llm = init_chat_model("openai:gpt-4.1", temperature=0.0).with_structured_output(UserPreferences)
- result = llm.invoke(
- [
- {"role": "system", "content": MEMORY_UPDATE_INSTRUCTIONS.format(current_profile=user_preferences.value, namespace=namespace)},
- {"role": "user", "content": f"Think carefully and update the memory profile based upon these user messages:"}
- ] + messages
- )
- # Save the updated memory to the store
- store.put(namespace, "user_preferences", result.preferences)
-
-# Nodes
-def triage_router(state: State, store: BaseStore) -> Command[Literal["triage_interrupt_handler", "response_agent", "__end__"]]:
- """Analyze email content to decide if we should respond, notify, or ignore.
-
- The triage step prevents the assistant from wasting time on:
- - Marketing emails and spam
- - Company-wide announcements
- - Messages meant for other teams
- """
-
- # Parse the email input
- author, to, subject, email_thread = parse_email(state["email_input"])
- user_prompt = triage_user_prompt.format(
- author=author, to=to, subject=subject, email_thread=email_thread
- )
-
- # Create email markdown for Agent Inbox in case of notification
- email_markdown = format_email_markdown(subject, author, to, email_thread)
-
- # Search for existing triage_preferences memory
- triage_instructions = get_memory(store, ("email_assistant", "triage_preferences"), default_triage_instructions)
-
- # Format system prompt with background and triage instructions
- system_prompt = triage_system_prompt.format(
- background=default_background,
- triage_instructions=triage_instructions,
- )
-
- # Run the router LLM
- result = llm_router.invoke(
- [
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_prompt},
- ]
- )
-
- # Decision
- classification = result.classification
-
- # Process the classification decision
- if classification == "respond":
- print("📧 Classification: RESPOND - This email requires a response")
- # Next node
- goto = "response_agent"
- # Update the state
- update = {
- "classification_decision": result.classification,
- "messages": [{"role": "user",
- "content": f"Respond to the email: {email_markdown}"
- }],
- }
-
- elif classification == "ignore":
- print("🚫 Classification: IGNORE - This email can be safely ignored")
-
- # Next node
- goto = END
- # Update the state
- update = {
- "classification_decision": classification,
- }
-
- elif classification == "notify":
- print("🔔 Classification: NOTIFY - This email contains important information")
-
- # Next node
- goto = "triage_interrupt_handler"
- # Update the state
- update = {
- "classification_decision": classification,
- }
-
- else:
- raise ValueError(f"Invalid classification: {classification}")
-
- return Command(goto=goto, update=update)
-
-def triage_interrupt_handler(state: State, store: BaseStore) -> Command[Literal["response_agent", "__end__"]]:
- """Handles interrupts from the triage step"""
-
- # Parse the email input
- author, to, subject, email_thread = parse_email(state["email_input"])
-
- # Create email markdown for Agent Inbox in case of notification
- email_markdown = format_email_markdown(subject, author, to, email_thread)
-
- # Create messages
- messages = [{"role": "user",
- "content": f"Email to notify user about: {email_markdown}"
- }]
-
- # Create interrupt for Agent Inbox
- request = {
- "action_request": {
- "action": f"Email Assistant: {state['classification_decision']}",
- "args": {}
- },
- "config": {
- "allow_ignore": True,
- "allow_respond": True,
- "allow_edit": False,
- "allow_accept": False,
- },
- # Email to show in Agent Inbox
- "description": email_markdown,
- }
-
- # Send to Agent Inbox and wait for response
- response = interrupt([request])[0]
-
- # If user provides feedback, go to response agent and use feedback to respond to email
- if response["type"] == "response":
- # Add feedback to messages
- user_input = response["args"]
- messages.append({"role": "user",
- "content": f"User wants to reply to the email. Use this feedback to respond: {user_input}"
- })
- # Update memory with feedback
- update_memory(store, ("email_assistant", "triage_preferences"), [{
- "role": "user",
- "content": f"The user decided to respond to the email, so update the triage preferences to capture this."
- }] + messages)
-
- goto = "response_agent"
-
- # If user ignores email, go to END
- elif response["type"] == "ignore":
- # Make note of the user's decision to ignore the email
- messages.append({"role": "user",
- "content": f"The user decided to ignore the email even though it was classified as notify. Update triage preferences to capture this."
- })
- # Update memory with feedback using the memory manager
- update_memory(store, ("email_assistant", "triage_preferences"), messages)
- goto = END
-
- # Catch all other responses
- else:
- raise ValueError(f"Invalid response: {response}")
-
- # Update the state
- update = {
- "messages": messages,
- }
-
- return Command(goto=goto, update=update)
-
-def llm_call(state: State, store: BaseStore):
- """LLM decides whether to call a tool or not"""
-
- # Search for existing cal_preferences memory
- cal_preferences = get_memory(store, ("email_assistant", "cal_preferences"), default_cal_preferences)
-
- # Search for existing response_preferences memory
- response_preferences = get_memory(store, ("email_assistant", "response_preferences"), default_response_preferences)
-
- return {
- "messages": [
- llm_with_tools.invoke(
- [
- {"role": "system", "content": agent_system_prompt_hitl_memory.format(
- tools_prompt=HITL_MEMORY_TOOLS_PROMPT,
- background=default_background,
- response_preferences=response_preferences,
- cal_preferences=cal_preferences
- )}
- ]
- + state["messages"]
- )
- ]
- }
-
-def interrupt_handler(state: State, store: BaseStore) -> Command[Literal["llm_call", "__end__"]]:
- """Creates an interrupt for human review of tool calls"""
-
- # Store messages
- result = []
-
- # Go to the LLM call node next
- goto = "llm_call"
-
- # Iterate over the tool calls in the last message
- for tool_call in state["messages"][-1].tool_calls:
-
- # Allowed tools for HITL
- hitl_tools = ["write_email", "schedule_meeting", "Question"]
-
- # If tool is not in our HITL list, execute it directly without interruption
- if tool_call["name"] not in hitl_tools:
-
- # Execute search_memory and other tools without interruption
- tool = tools_by_name[tool_call["name"]]
- observation = tool.invoke(tool_call["args"])
- result.append({"role": "tool", "content": observation, "tool_call_id": tool_call["id"]})
- continue
-
- # Get original email from email_input in state
- email_input = state["email_input"]
- author, to, subject, email_thread = parse_email(email_input)
- original_email_markdown = format_email_markdown(subject, author, to, email_thread)
-
- # Format tool call for display and prepend the original email
- tool_display = format_for_display(state, tool_call)
- description = original_email_markdown + tool_display
-
- # Configure what actions are allowed in Agent Inbox
- if tool_call["name"] == "write_email":
- config = {
- "allow_ignore": True,
- "allow_respond": True,
- "allow_edit": True,
- "allow_accept": True,
- }
- elif tool_call["name"] == "schedule_meeting":
- config = {
- "allow_ignore": True,
- "allow_respond": True,
- "allow_edit": True,
- "allow_accept": True,
- }
- elif tool_call["name"] == "Question":
- config = {
- "allow_ignore": True,
- "allow_respond": True,
- "allow_edit": False,
- "allow_accept": False,
- }
- else:
- raise ValueError(f"Invalid tool call: {tool_call['name']}")
-
- # Create the interrupt request
- request = {
- "action_request": {
- "action": tool_call["name"],
- "args": tool_call["args"]
- },
- "config": config,
- "description": description,
- }
-
- # Send to Agent Inbox and wait for response
- response = interrupt([request])[0]
-
- # Handle the responses
- if response["type"] == "accept":
-
- # Execute the tool with original args
- tool = tools_by_name[tool_call["name"]]
- observation = tool.invoke(tool_call["args"])
- result.append({"role": "tool", "content": observation, "tool_call_id": tool_call["id"]})
-
- elif response["type"] == "edit":
-
- # Tool selection
- tool = tools_by_name[tool_call["name"]]
-
- # Get edited args from Agent Inbox
- edited_args = response["args"]["args"]
-
- # Save feedback in memory and update the write_email tool call with the edited content from Agent Inbox
- if tool_call["name"] == "write_email":
-
- # Capture the initial tool call
- initial_tool_call = tool_call["name"] + ": " + str(tool_call["args"])
-
- # Update the AI message's tool call with edited content (reference to the message in the state)
- ai_message = state["messages"][-1]
- current_id = tool_call["id"]
-
- # Replace the original tool call with the edited one (any changes made to this reference affect the original object in the state)
- ai_message.tool_calls = [tc for tc in ai_message.tool_calls if tc["id"] != current_id] + [
- {"type": "tool_call", "name": tool_call["name"], "args": edited_args, "id": current_id}
- ]
-
- # Execute the tool with edited args
- observation = tool.invoke(edited_args)
-
- # Add only the tool response message
- result.append({"role": "tool", "content": observation, "tool_call_id": current_id})
-
- # We update the memory
- update_memory(store, ("email_assistant", "response_preferences"), [{
- "role": "user",
- "content": f"User edited the email response. Here is the initial email generated by the assistant: {initial_tool_call}. Here is the edited email: {edited_args}. Follow all instructions above, and remember: {MEMORY_UPDATE_INSTRUCTIONS_REINFORCEMENT}."
- }])
-
- # Save feedback in memory and update the schedule_meeting tool call with the edited content from Agent Inbox
- elif tool_call["name"] == "schedule_meeting":
-
- # Capture the initial tool call
- initial_tool_call = tool_call["name"] + ": " + str(tool_call["args"])
-
- # Update the AI message's tool call with edited content
- ai_message = state["messages"][-1]
- current_id = tool_call["id"]
-
- # Replace the original tool call with the edited one
- ai_message.tool_calls = [tc for tc in ai_message.tool_calls if tc["id"] != current_id] + [
- {"type": "tool_call", "name": tool_call["name"], "args": edited_args, "id": current_id}
- ]
-
- # Execute the tool with edited args
- observation = tool.invoke(edited_args)
-
- # Add only the tool response message
- result.append({"role": "tool", "content": observation, "tool_call_id": current_id})
-
- # Update the memory
- update_memory(store, ("email_assistant", "cal_preferences"), [{
- "role": "user",
- "content": f"User edited the calendar invitation. Here is the initial calendar invitation generated by the assistant: {initial_tool_call}. Here is the edited calendar invitation: {edited_args}. Follow all instructions above, and remember: {MEMORY_UPDATE_INSTRUCTIONS_REINFORCEMENT}."
- }])
-
- # Catch all other tool calls
- else:
- raise ValueError(f"Invalid tool call: {tool_call['name']}")
-
- elif response["type"] == "ignore":
-
- if tool_call["name"] == "write_email":
- # Don't execute the tool, and tell the agent how to proceed
- result.append({"role": "tool", "content": "User ignored this email draft. Ignore this email and end the workflow.", "tool_call_id": tool_call["id"]})
- # Go to END
- goto = END
- # Update the memory by reflecting on the email tool call
- update_memory(store, ("email_assistant", "triage_preferences"), state["messages"] + result + [{
- "role": "user",
- "content": f"The user ignored the email draft. That means they did not want to respond to the email. Update the triage preferences to ensure emails of this type are not classified as respond. Follow all instructions above, and remember: {MEMORY_UPDATE_INSTRUCTIONS_REINFORCEMENT}."
- }])
-
- elif tool_call["name"] == "schedule_meeting":
- # Don't execute the tool, and tell the agent how to proceed
- result.append({"role": "tool", "content": "User ignored this calendar meeting draft. Ignore this email and end the workflow.", "tool_call_id": tool_call["id"]})
- # Go to END
- goto = END
- # Update the memory by reflecting on the full message history including the schedule_meeting tool call
- update_memory(store, ("email_assistant", "triage_preferences"), state["messages"] + result + [{
- "role": "user",
- "content": f"The user ignored the calendar meeting draft. That means they did not want to schedule a meeting for this email. Update the triage preferences to ensure emails of this type are not classified as respond. Follow all instructions above, and remember: {MEMORY_UPDATE_INSTRUCTIONS_REINFORCEMENT}."
- }])
-
- elif tool_call["name"] == "Question":
- # Don't execute the tool, and tell the agent how to proceed
- result.append({"role": "tool", "content": "User ignored this question. Ignore this email and end the workflow.", "tool_call_id": tool_call["id"]})
- # Go to END
- goto = END
- # Update the memory by reflecting on the full message history including the Question tool call
- update_memory(store, ("email_assistant", "triage_preferences"), state["messages"] + result + [{
- "role": "user",
- "content": f"The user ignored the Question. That means they did not want to answer the question or deal with this email. Update the triage preferences to ensure emails of this type are not classified as respond. Follow all instructions above, and remember: {MEMORY_UPDATE_INSTRUCTIONS_REINFORCEMENT}."
- }])
-
- else:
- raise ValueError(f"Invalid tool call: {tool_call['name']}")
-
- elif response["type"] == "response":
- # User provided feedback
- user_feedback = response["args"]
- if tool_call["name"] == "write_email":
- # Don't execute the tool, and add a message with the user feedback to incorporate into the email
- result.append({"role": "tool", "content": f"User gave feedback, which can we incorporate into the email. Feedback: {user_feedback}", "tool_call_id": tool_call["id"]})
- update_memory(store, ("email_assistant", "response_preferences"), state["messages"] + result + [{
- "role": "user",
- "content": f"User gave feedback, which we can use to update the response preferences. Follow all instructions above, and remember: {MEMORY_UPDATE_INSTRUCTIONS_REINFORCEMENT}."
- }])
-
- elif tool_call["name"] == "schedule_meeting":
- # Don't execute the tool, and add a message with the user feedback to incorporate into the email
- result.append({"role": "tool", "content": f"User gave feedback, which can we incorporate into the meeting request. Feedback: {user_feedback}", "tool_call_id": tool_call["id"]})
- update_memory(store, ("email_assistant", "cal_preferences"), state["messages"] + result + [{
- "role": "user",
- "content": f"User gave feedback, which we can use to update the calendar preferences. Follow all instructions above, and remember: {MEMORY_UPDATE_INSTRUCTIONS_REINFORCEMENT}."
- }])
-
- elif tool_call["name"] == "Question":
- # Don't execute the tool, and add a message with the user feedback to incorporate into the email
- result.append({"role": "tool", "content": f"User answered the question, which can we can use for any follow up actions. Feedback: {user_feedback}", "tool_call_id": tool_call["id"]})
-
- else:
- raise ValueError(f"Invalid tool call: {tool_call['name']}")
-
- # Update the state
- update = {
- "messages": result,
- }
-
- return Command(goto=goto, update=update)
-
-# Conditional edge function
-def should_continue(state: State, store: BaseStore) -> Literal["interrupt_handler", END]:
- """Route to tool handler, or end if Done tool called"""
- messages = state["messages"]
- last_message = messages[-1]
- if last_message.tool_calls:
- for tool_call in last_message.tool_calls:
- if tool_call["name"] == "Done":
- # TODO: Here, we could update the background memory with the email-response for follow up actions.
- return END
- else:
- return "interrupt_handler"
-
-# Build workflow
-agent_builder = StateGraph(State)
-
-# Add nodes - with store parameter
-agent_builder.add_node("llm_call", llm_call)
-agent_builder.add_node("interrupt_handler", interrupt_handler)
-
-# Add edges
-agent_builder.add_edge(START, "llm_call")
-agent_builder.add_conditional_edges(
- "llm_call",
- should_continue,
- {
- "interrupt_handler": "interrupt_handler",
- END: END,
- },
-)
-
-# Compile the agent
-response_agent = agent_builder.compile()
-
-# Build overall workflow with store and checkpointer
-overall_workflow = (
- StateGraph(State, input=StateInput)
- .add_node(triage_router)
- .add_node(triage_interrupt_handler)
- .add_node("response_agent", response_agent)
- .add_edge(START, "triage_router")
-)
-
-email_assistant = overall_workflow.compile()
\ No newline at end of file
diff --git a/python/prompts.py b/python/prompts.py
deleted file mode 100644
index acfc768..0000000
--- a/python/prompts.py
+++ /dev/null
@@ -1,262 +0,0 @@
-# Baseline agent prompt
-from datetime import datetime
-
-from src.email_assistant.tools.default.prompt_templates import (
- STANDARD_TOOLS_PROMPT,
- HITL_TOOLS_PROMPT,
- HITL_MEMORY_TOOLS_PROMPT,
- AGENT_TOOLS_PROMPT
-)
-
-agent_system_prompt_baseline = """
-< Role >
-You are a top-notch executive assistant who cares about helping your executive perform as well as possible.
- Role >
-
-< Tools >
-You have access to the following tools to help manage communications and schedule:
-{tools_prompt}
- Tools >
-
-< Instructions >
-When handling emails, follow these steps:
-1. Carefully analyze the email content and purpose
-2. IMPORTANT --- always call a tool and call one tool at a time until the task is complete:
-3. For responding to the email, draft a response email with the write_email tool
-4. For meeting requests, use the check_calendar_availability tool to find open time slots
-5. To schedule a meeting, use the schedule_meeting tool with a datetime object for the preferred_day parameter
- - Today's date is """ + datetime.now().strftime("%Y-%m-%d") + """ - use this for scheduling meetings accurately
-6. If you scheduled a meeting, then draft a short response email using the write_email tool
-7. After using the write_email tool, the task is complete
-8. If you have sent the email, then use the Done tool to indicate that the task is complete
- Instructions >
-
-< Triage Instructions >
-{triage_instructions}
- Triage Instructions >
-
-< Background >
-{background}
- Background >
-
-< Response Preferences >
-{response_preferences}
- Response Preferences >
-
-< Calendar Preferences >
-{cal_preferences}
- Calendar Preferences >
-"""
-
-# Agentic workflow triage prompt
-triage_system_prompt = """
-
-< Role >
-Your role is to triage incoming emails based upon instructs and background information below.
- Role >
-
-< Background >
-{background}.
- Background >
-
-< Instructions >
-Categorize each email into one of three categories:
-1. IGNORE - Emails that are not worth responding to or tracking
-2. NOTIFY - Important information that worth notification but doesn't require a response
-3. RESPOND - Emails that need a direct response
-Classify the below email into one of these categories.
- Instructions >
-
-< Rules >
-{triage_instructions}
- Rules >
-"""
-
-# Agentic workflow triage user prompt
-triage_user_prompt = """
-Please determine how to handle the below email thread:
-
-From: {author}
-To: {to}
-Subject: {subject}
-{email_thread}"""
-
-# Agentic workflow prompt
-agent_system_prompt = """
-< Role >
-You are a top-notch executive assistant who cares about helping your executive perform as well as possible.
- Role >
-
-< Tools >
-You have access to the following tools to help manage communications and schedule:
-{tools_prompt}
- Tools >
-
-< Instructions >
-When handling emails, follow these steps:
-1. Carefully analyze the email content and purpose
-2. IMPORTANT --- always call a tool and call one tool at a time until the task is complete:
-3. For responding to the email, draft a response email with the write_email tool
-4. For meeting requests, use the check_calendar_availability tool to find open time slots
-5. To schedule a meeting, use the schedule_meeting tool with a datetime object for the preferred_day parameter
- - Today's date is """ + datetime.now().strftime("%Y-%m-%d") + """ - use this for scheduling meetings accurately
-6. If you scheduled a meeting, then draft a short response email using the write_email tool
-7. After using the write_email tool, the task is complete
-8. If you have sent the email, then use the Done tool to indicate that the task is complete
- Instructions >
-
-< Background >
-{background}
- Background >
-
-< Response Preferences >
-{response_preferences}
- Response Preferences >
-
-< Calendar Preferences >
-{cal_preferences}
- Calendar Preferences >
-"""
-
-# Agentic workflow with HITL prompt
-agent_system_prompt_hitl = """
-< Role >
-You are a top-notch executive assistant who cares about helping your executive perform as well as possible.
- Role >
-
-< Tools >
-You have access to the following tools to help manage communications and schedule:
-{tools_prompt}
- Tools >
-
-< Instructions >
-When handling emails, follow these steps:
-1. Carefully analyze the email content and purpose
-2. IMPORTANT --- always call a tool and call one tool at a time until the task is complete:
-3. If you need more information to complete the task, use the Question tool to ask a follow-up question to the user
-4. For responding to the email, draft a response email with the write_email tool
-5. For meeting requests, use the check_calendar_availability tool to find open time slots
-6. To schedule a meeting, use the schedule_meeting tool with a datetime object for the preferred_day parameter
- - Today's date is """ + datetime.now().strftime("%Y-%m-%d") + """ - use this for scheduling meetings accurately
-7. If you scheduled a meeting, then draft a short response email using the write_email tool
-8. After using the write_email tool, the task is complete
-9. If you have sent the email, then use the Done tool to indicate that the task is complete
- Instructions >
-
-< Background >
-{background}
- Background >
-
-< Response Preferences >
-{response_preferences}
- Response Preferences >
-
-< Calendar Preferences >
-{cal_preferences}
- Calendar Preferences >
-"""
-
-# Agentic workflow with HITL and memory prompt
-agent_system_prompt_hitl_memory = """
-< Role >
-You are a top-notch executive assistant.
- Role >
-
-< Tools >
-You have access to the following tools to help manage communications and schedule:
-{tools_prompt}
- Tools >
-
-< Instructions >
-When handling emails, follow these steps:
-1. Carefully analyze the email content and purpose
-2. IMPORTANT --- always call a tool and call one tool at a time until the task is complete:
-3. To gather information background information use the "background" tool
-4. To gather information about meeting preferences use the "cal_preferences" tool
-5. To gather information about response preferences use the "response_preferences" tool
-6. If the provided background information, meeting preferences, or response preferences are not sufficient, use the Question tool to ask follow-up questions
-7. For meeting requests, use the check_calendar_availability tool to find open time slots
-8. Schedule meetings with the schedule_meeting tool when appropriate
- - Today's date is """ + datetime.now().strftime("%Y-%m-%d") + """ - use this for scheduling meetings accurately
-9. If you scheduled a meeting, then draft a short response email using the write_email tool
-10. Draft response emails using the write_email tool
-11. After calling the write_email tool, the task is complete
-12. If you have sent the email, then use the Done tool to indicate that the task is complete
- Instructions >
-
-< Response Preferences >
-{response_preferences}
- Response Preferences >
-
-< Calendar Preferences >
-{cal_preferences}
- Calendar Preferences >
-
-< Background >
-{background}
- Background >
-"""
-
-# Default background information
-default_background = """
-I'm Lance, a software engineer at LangChain.
-"""
-
-# Default response preferences
-default_response_preferences = """
-Use professional and concise language. If the e-mail mentions a deadline, make sure to explicitly acknowledge and reference the deadline in your response.
-
-When responding to technical questions that require investigation:
-- Clearly state whether you will investigate or who you will ask
-- Provide an estimated timeline for when you'll have more information or complete the task
-
-When responding to event or conference invitations:
-- Always acknowledge any mentioned deadlines (particularly registration deadlines)
-- If workshops or specific topics are mentioned, ask for more specific details about them
-- If discounts (group or early bird) are mentioned, explicitly request information about them
-- Don't commit
-
-When responding to collaboration or project-related requests:
-- Acknowledge any existing work or materials mentioned (drafts, slides, documents, etc.)
-- Explicitly mention reviewing these materials before or during the meeting
-- When scheduling meetings, clearly state the specific day, date, and time proposed
-
-When responding to meeting scheduling requests:
-- If times are proposed, verify calendar availability for all time slots mentioned in the original email and then commit to one of the proposed times based on your availability by scheduling the meeting. Or, say you can't make it at the time proposed.
-- If no times are proposed, then check your calendar for availability and propose multiple time options when available instead of selecting just one.
-- Mention the meeting duration in your response to confirm you've noted it correctly.
-- Reference the meeting's purpose in your response.
-"""
-
-# Default calendar preferences
-default_cal_preferences = """
-30 minute meetings are preferred, but 15 minute meetings are also acceptable.
-"""
-
-# Default triage instructions
-default_triage_instructions = """
-Emails that are not worth responding to:
-- Marketing newsletters and promotional emails
-- Spam or suspicious emails
-- CC'd on FYI threads with no direct questions
-
-There are also other things that should be known about, but don't require an email response. For these, you should notify (using the `notify` response). Examples of this include:
-- Team member out sick or on vacation
-- Build system notifications or deployments
-- Project status updates without action items
-- Important company announcements
-- FYI emails that contain relevant information for current projects
-- HR Department deadline reminders
-- Subscription status / renewal reminders
-- GitHub notifications
-
-Emails that are worth responding to:
-- Direct questions from team members requiring expertise
-- Meeting requests requiring confirmation
-- Critical bug reports related to team's projects
-- Requests from management requiring acknowledgment
-- Client inquiries about project status or features
-- Technical questions about documentation, code, or APIs (especially questions about missing endpoints or features)
-- Personal reminders related to family (wife / daughter)
-- Personal reminder related to self-care (doctor appointments, etc)
-"""
\ No newline at end of file
diff --git a/python/schemas.py b/python/schemas.py
deleted file mode 100644
index 29065e5..0000000
--- a/python/schemas.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from pydantic import BaseModel, Field
-from typing import Optional
-from typing_extensions import TypedDict, Literal, Annotated
-from langgraph.graph import MessagesState
-
-class RouterSchema(BaseModel):
- """Analyze the unread email and route it according to its content."""
-
- reasoning: str = Field(
- description="Step-by-step reasoning behind the classification."
- )
- classification: Literal["ignore", "respond", "notify"] = Field(
- description="The classification of an email: 'ignore' for irrelevant emails, "
- "'notify' for important information that doesn't need a response, "
- "'respond' for emails that need a reply",
- )
-
-class StateInput(TypedDict):
- # This is the input to the state
- email_input: dict
-
-class State(MessagesState):
- # This state class has the messages key build in
- email_input: dict
- classification_decision: Literal["ignore", "respond", "notify"]
-
-class EmailData(TypedDict):
- id: str
- thread_id: str
- from_email: str
- subject: str
- page_content: str
- send_time: str
- to_email: str
\ No newline at end of file
diff --git a/python/tools/__init__.py b/python/tools/__init__.py
deleted file mode 100644
index 0ab591c..0000000
--- a/python/tools/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from src.email_assistant.tools.base import get_tools, get_tools_by_name
-from src.email_assistant.tools.default.email_tools import write_email, triage_email, Done
-from src.email_assistant.tools.default.calendar_tools import schedule_meeting, check_calendar_availability
-
-__all__ = [
- "get_tools",
- "get_tools_by_name",
- "write_email",
- "triage_email",
- "Done",
- "schedule_meeting",
- "check_calendar_availability",
-]
\ No newline at end of file
diff --git a/python/tools/__pycache__/__init__.cpython-311.pyc b/python/tools/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index 734337c..0000000
Binary files a/python/tools/__pycache__/__init__.cpython-311.pyc and /dev/null differ
diff --git a/python/tools/__pycache__/base.cpython-311.pyc b/python/tools/__pycache__/base.cpython-311.pyc
deleted file mode 100644
index 337d9a5..0000000
Binary files a/python/tools/__pycache__/base.cpython-311.pyc and /dev/null differ
diff --git a/python/tools/base.py b/python/tools/base.py
deleted file mode 100644
index 50d4601..0000000
--- a/python/tools/base.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from typing import Dict, List, Callable, Any
-from langchain_core.tools import BaseTool
-
-def get_tools(tool_names: List[str] = None, include_gmail: bool = False) -> List[BaseTool]:
- """Get specified tools or all tools if tool_names is None.
-
- Args:
- tool_names: Optional list of tool names to include. If None, returns all tools.
- include_gmail: Whether to include Gmail tools. Defaults to False.
-
- Returns:
- List of tool objects
- """
- # Import default tools
- from src.email_assistant.tools.default.email_tools import write_email, triage_email, Done
- from src.email_assistant.tools.default.calendar_tools import schedule_meeting, check_calendar_availability
-
- # Base tools dictionary
- all_tools = {
- "write_email": write_email,
- "triage_email": triage_email,
- "Done": Done,
- "schedule_meeting": schedule_meeting,
- "check_calendar_availability": check_calendar_availability,
- }
-
- # Add Gmail tools if requested
- if include_gmail:
- try:
- from src.email_assistant.tools.gmail.gmail_tools import (
- fetch_emails_tool,
- send_email_tool,
- check_calendar_tool,
- schedule_meeting_tool
- )
-
- all_tools.update({
- "fetch_emails_tool": fetch_emails_tool,
- "send_email_tool": send_email_tool,
- "check_calendar_tool": check_calendar_tool,
- "schedule_meeting_tool": schedule_meeting_tool,
- })
- except ImportError:
- # If Gmail tools aren't available, continue without them
- pass
-
- if tool_names is None:
- return list(all_tools.values())
-
- return [all_tools[name] for name in tool_names if name in all_tools]
-
-def get_tools_by_name(tools: List[BaseTool] = None) -> Dict[str, BaseTool]:
- """Get a dictionary of tools mapped by name."""
- if tools is None:
- tools = get_tools()
-
- return {tool.name: tool for tool in tools}
diff --git a/python/tools/default/__init__.py b/python/tools/default/__init__.py
deleted file mode 100644
index 7e0a5c1..0000000
--- a/python/tools/default/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""Default tools for email assistant."""
-
-from src.email_assistant.tools.default.email_tools import write_email, triage_email, Done
-from src.email_assistant.tools.default.calendar_tools import schedule_meeting, check_calendar_availability
-from src.email_assistant.tools.default.prompt_templates import (
- STANDARD_TOOLS_PROMPT,
- AGENT_TOOLS_PROMPT,
- HITL_TOOLS_PROMPT,
- HITL_MEMORY_TOOLS_PROMPT
-)
-
-__all__ = [
- "write_email",
- "triage_email",
- "Done",
- "schedule_meeting",
- "check_calendar_availability",
- "STANDARD_TOOLS_PROMPT",
- "AGENT_TOOLS_PROMPT",
- "HITL_TOOLS_PROMPT",
- "HITL_MEMORY_TOOLS_PROMPT"
-]
\ No newline at end of file
diff --git a/python/tools/default/__pycache__/__init__.cpython-311.pyc b/python/tools/default/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index db3c532..0000000
Binary files a/python/tools/default/__pycache__/__init__.cpython-311.pyc and /dev/null differ
diff --git a/python/tools/default/__pycache__/calendar_tools.cpython-311.pyc b/python/tools/default/__pycache__/calendar_tools.cpython-311.pyc
deleted file mode 100644
index ba4efe5..0000000
Binary files a/python/tools/default/__pycache__/calendar_tools.cpython-311.pyc and /dev/null differ
diff --git a/python/tools/default/__pycache__/email_tools.cpython-311.pyc b/python/tools/default/__pycache__/email_tools.cpython-311.pyc
deleted file mode 100644
index ea54046..0000000
Binary files a/python/tools/default/__pycache__/email_tools.cpython-311.pyc and /dev/null differ
diff --git a/python/tools/default/__pycache__/prompt_templates.cpython-311.pyc b/python/tools/default/__pycache__/prompt_templates.cpython-311.pyc
deleted file mode 100644
index 6a5912d..0000000
Binary files a/python/tools/default/__pycache__/prompt_templates.cpython-311.pyc and /dev/null differ
diff --git a/python/tools/default/calendar_tools.py b/python/tools/default/calendar_tools.py
deleted file mode 100644
index 00eb563..0000000
--- a/python/tools/default/calendar_tools.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from datetime import datetime
-from langchain_core.tools import tool
-
-@tool
-def schedule_meeting(
- attendees: list[str], subject: str, duration_minutes: int, preferred_day: datetime, start_time: int
-) -> str:
- """Schedule a calendar meeting."""
- # Placeholder response - in real app would check calendar and schedule
- date_str = preferred_day.strftime("%A, %B %d, %Y")
- return f"Meeting '{subject}' scheduled on {date_str} at {start_time} for {duration_minutes} minutes with {len(attendees)} attendees"
-
-@tool
-def check_calendar_availability(day: str) -> str:
- """Check calendar availability for a given day."""
- # Placeholder response - in real app would check actual calendar
- return f"Available times on {day}: 9:00 AM, 2:00 PM, 4:00 PM"
diff --git a/python/tools/default/email_tools.py b/python/tools/default/email_tools.py
deleted file mode 100644
index b6eebbb..0000000
--- a/python/tools/default/email_tools.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Literal
-from pydantic import BaseModel
-from langchain_core.tools import tool
-
-@tool
-def write_email(to: str, subject: str, content: str) -> str:
- """Write and send an email."""
- # Placeholder response - in real app would send email
- return f"Email sent to {to} with subject '{subject}'"
-
-@tool
-def triage_email(category: Literal["ignore", "notify", "respond"]) -> str:
- """Triage an email into one of three categories: ignore, notify, respond."""
- return f"Classification Decision: {category}"
-
-@tool
-class Done(BaseModel):
- """E-mail has been sent."""
- done: bool
diff --git a/python/tools/default/prompt_templates.py b/python/tools/default/prompt_templates.py
deleted file mode 100644
index df9b0cc..0000000
--- a/python/tools/default/prompt_templates.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""Tool prompt templates for the email assistant."""
-
-# Standard tool descriptions for insertion into prompts
-STANDARD_TOOLS_PROMPT = """
-1. triage_email(ignore, notify, respond) - Triage emails into one of three categories
-2. write_email(to, subject, content) - Send emails to specified recipients
-3. schedule_meeting(attendees, subject, duration_minutes, preferred_day, start_time) - Schedule calendar meetings where preferred_day is a datetime object
-4. check_calendar_availability(day) - Check available time slots for a given day
-5. Done - E-mail has been sent
-"""
-
-# Tool descriptions for HITL workflow
-HITL_TOOLS_PROMPT = """
-1. write_email(to, subject, content) - Send emails to specified recipients
-2. schedule_meeting(attendees, subject, duration_minutes, preferred_day, start_time) - Schedule calendar meetings where preferred_day is a datetime object
-3. check_calendar_availability(day) - Check available time slots for a given day
-4. Question(content) - Ask the user any follow-up questions
-5. Done - E-mail has been sent
-"""
-
-# Tool descriptions for HITL with memory workflow
-HITL_MEMORY_TOOLS_PROMPT = """
-1. write_email(to, subject, content) - Send emails to specified recipients
-2. schedule_meeting(attendees, subject, duration_minutes, preferred_day, start_time) - Schedule calendar meetings where preferred_day is a datetime object
-3. check_calendar_availability(day) - Check available time slots for a given day
-4. Question(content) - Ask the user any follow-up questions
-5. background - Search for background information about the user and their contacts
-6. Done - E-mail has been sent
-"""
-
-# Tool descriptions for agent workflow without triage
-AGENT_TOOLS_PROMPT = """
-1. write_email(to, subject, content) - Send emails to specified recipients
-2. schedule_meeting(attendees, subject, duration_minutes, preferred_day, start_time) - Schedule calendar meetings where preferred_day is a datetime object
-3. check_calendar_availability(day) - Check available time slots for a given day
-4. Done - E-mail has been sent
-"""
\ No newline at end of file
diff --git a/python/tools/gmail/README.md b/python/tools/gmail/README.md
deleted file mode 100644
index 2d33446..0000000
--- a/python/tools/gmail/README.md
+++ /dev/null
@@ -1,303 +0,0 @@
-# Gmail Integration Tools
-
-This directory contains tools for integrating with Gmail and Google Calendar APIs to enable the email assistant to work with real emails and calendar events.
-
-## Features
-
-- **Email Fetching**: Retrieve recent emails from your Gmail account
-- **Email Sending**: Send replies to email threads
-- **Calendar Availability**: Check your Google Calendar for availability on specific dates
-- **Meeting Scheduling**: Create calendar events and send invites to attendees
-
-## Setup Instructions
-
-### 1. Set up Google Cloud Project and Enable Gmail API
-
-1. Enable the Gmail API by clicking the blue "Enable API" button [here](https://developers.google.com/gmail/api/quickstart/python#enable_the_api)
-
-2. Authorize credentials for a desktop application [here](https://developers.google.com/workspace/gmail/api/quickstart/python#authorize_credentials_for_a_desktop_application)
-- Go to Clients
-- Create Client
-- Application type > Desktop app
-- Create
-- Under "Audience" select "External" if you're using a personal email (non-Google Workspace)
-
-
-
-- Add yourself as a test user
-
-
-
-3. Save the downloaded JSON file
-
-### 3. Set Up Authentication Files
-
-1. Move your downloaded client secret JSON file to the `.secrets` directory
-
-```bash
-# Create a secrets directory
-mkdir -p src/email_assistant/tools/gmail/.secrets
-
-# Move your downloaded client secret to the secrets directory
-mv /path/to/downloaded/client_secret.json src/email_assistant/tools/gmail/.secrets/secrets.json
-```
-
-2. Run the Gmail setup script
-
-```bash
-# Run the Gmail setup script
-python src/email_assistant/tools/gmail/setup_gmail.py
-```
-
-- This will open a browser window for you to authenticate with your Google account
-- This will create a `token.json` file in the `.secrets` directory
-- This token will be used for Gmail API access
-
-### 4. Run the Gmail Ingestion Script
-
-1. Once you have authentication set up, you can run the Gmail ingestion script.
-
-2. Start the locally running LangGraph server in one terminal:
-
-```
-langgraph dev
-```
-
-3. Run the ingestion script in another terminal:
-
-```bash
-python src/email_assistant/tools/gmail/run_ingest.py --email rlance.martin@gmail.com --minutes-since 1000
-```
-
-- This will fetch emails from the past 1000 minutes and process them with your email assistant.
-- It will use the LangGraph SDK to pass each email to the locally running email assistant.
-
-#### Important Ingestion Parameters:
-
-- `--graph-name`: Name of the LangGraph to use (default: "email_assistant_hitl_memory")
-- `--email`: The email address to fetch messages from (alternative to setting EMAIL_ADDRESS)
-- `--minutes-since`: Only process emails that are newer than this many minutes (default: 60)
-- `--url`: URL of the LangGraph deployment (default: http://127.0.0.1:2024)
-- `--rerun`: Process emails that have already been processed (default: false)
-- `--early`: Stop after processing one email (default: false)
-- `--mock`: Run in mock mode without requiring a LangGraph server
-- `--include-read`: Include emails that have already been read (by default only unread emails are processed)
-- `--skip-filters`: Process all emails without filtering (by default only latest messages in threads where you're not the sender are processed)
-- `--enable-tracing`: Enable LangSmith tracing (requires LANGCHAIN_API_KEY to be set)
-- `--langsmith-api-key`: LangSmith API key for tracing (alternative to setting LANGCHAIN_API_KEY)
-- `--langsmith-project`: LangSmith project name for tracing (default: "gmail-assistant")
-
-#### Flag Combinations:
-
-- `--rerun --early`: Process one email (regardless if it was processed before) and stop
-- `--rerun`: Process all emails, including ones previously processed by LangGraph
-- `--early`: Process only one new (previously unprocessed) email and stop
-- (no flags): Process only new (previously unprocessed) emails
-- `--include-read --skip-filters`: Process all emails, including ones marked as read and ones that would normally be filtered out
-- `--minutes-since 1000 --include-read --skip-filters`: Process all emails from the past ~16 hours without any filtering
-- `--enable-tracing --langsmith-project "my-project"`: Process emails with LangSmith tracing enabled
-
-#### Troubleshooting:
-
-- **Missing emails?** The Gmail API applies filters to show only important/primary emails by default. You can:
- - Increase the `--minutes-since` parameter to a larger value (e.g., 1000) to fetch emails from a longer time period
- - Use the `--include-read` flag to process emails marked as "read" (by default only unread emails are processed)
- - Use the `--skip-filters` flag to include all messages (not just the latest in a thread, and including ones you sent)
- - Try running with all options to process everything: `--include-read --skip-filters --minutes-since 1000`
- - Use the `--mock` flag to test the system with simulated emails
-
-## How Gmail Ingestion Works
-
-The Gmail ingestion process works in three main stages:
-
-### 1. CLI Parameters → Gmail Search Query
-
-CLI parameters are translated into a Gmail search query:
-
-- `--minutes-since 1440` → `after:TIMESTAMP` (emails from the last 24 hours)
-- `--email you@example.com` → `to:you@example.com OR from:you@example.com` (emails where you're sender or recipient)
-- `--include-read` → removes `is:unread` filter (includes read messages)
-
-For example, running:
-```
-python run_ingest.py --email you@example.com --minutes-since 1440 --include-read
-```
-
-Creates a Gmail API search query like:
-```
-(to:you@example.com OR from:you@example.com) after:1745432245
-```
-
-### 2. Search Results → Thread Processing
-
-For each message returned by the search:
-
-1. The script obtains the thread ID
-2. Using this thread ID, it fetches the **complete thread** with all messages
-3. Messages in the thread are sorted by date to identify the latest message
-4. Depending on filtering options, it processes either:
- - The specific message found in the search (default behavior)
- - The latest message in the thread (when using `--skip-filters`)
-
-### 3. Default Filters and `--skip-filters` Behavior
-
-#### Default Filters Applied
-
-Without `--skip-filters`, the system applies these three filters in sequence:
-
-1. **Unread Filter** (controlled by `--include-read`):
- - Default behavior: Only processes unread messages
- - With `--include-read`: Processes both read and unread messages
- - Implementation: Adds `is:unread` to the Gmail search query
- - This filter happens at the search level before any messages are retrieved
-
-2. **Sender Filter**:
- - Default behavior: Skips messages sent by your own email address
- - Implementation: Checks if your email appears in the "From" header
- - Logic: `is_from_user = email_address in from_header`
- - This prevents the assistant from responding to your own emails
-
-3. **Thread-Position Filter**:
- - Default behavior: Only processes the most recent message in each thread
- - Implementation: Compares message ID with the last message in thread
- - Logic: `is_latest_in_thread = message["id"] == last_message["id"]`
- - Prevents processing older messages when a newer reply exists
-
-The combination of these filters means only the latest message in each thread that was not sent by you and is unread (unless `--include-read` is specified) will be processed.
-
-#### Effect of `--skip-filters` Flag
-
-When `--skip-filters` is enabled:
-
-1. **Bypasses Sender and Thread-Position Filters**:
- - Messages sent by you will be processed
- - Messages that aren't the latest in thread will be processed
- - Logic: `should_process = skip_filters or (not is_from_user and is_latest_in_thread)`
-
-2. **Changes Which Message Is Processed**:
- - Without `--skip-filters`: Uses the specific message found by search
- - With `--skip-filters`: Always uses the latest message in the thread
- - Even if the latest message wasn't found in the search results
-
-3. **Unread Filter Still Applies (unless overridden)**:
- - `--skip-filters` does NOT bypass the unread filter
- - To process read messages, you must still use `--include-read`
- - This is because the unread filter happens at the search level
-
-In summary:
-- Default: Process only unread messages where you're not the sender and that are the latest in their thread
-- `--skip-filters`: Process all messages found by search, using the latest message in each thread
-- `--include-read`: Include read messages in the search
-- `--include-read --skip-filters`: Most comprehensive, processes the latest message in all threads found by search
-
-## Important Gmail API Limitations
-
-The Gmail API has several limitations that affect email ingestion:
-
-1. **Search-Based API**: Gmail doesn't provide a direct "get all emails from timeframe" endpoint
- - All email retrieval relies on Gmail's search functionality
- - Search results can be delayed for very recent messages (indexing lag)
- - Search results might not include all messages that technically match criteria
-
-2. **Two-Stage Retrieval Process**:
- - Initial search to find relevant message IDs
- - Secondary thread retrieval to get complete conversations
- - This two-stage process is necessary because search doesn't guarantee complete thread information
-
-## When to Use `--skip-filters`
-
-### Use `--skip-filters` When:
-
-- **Latest Messages Are Missing**: The thread contains newer messages that aren't being processed
-- **Complete Thread Context Needed**: You want to ensure you have the most up-to-date conversation context
-- **Debugging Thread Issues**: You need to see which messages exist in threads vs. which are being processed
-- **Initial Data Loading**: You're populating the system with existing conversations
-- **Inconsistent Results**: You notice some messages are being skipped or processed out of order
-
-### When NOT to Use `--skip-filters`:
-
-- **Day-to-Day Operation**: For routine email processing, the default filters provide a natural workflow
-- **Avoiding Duplicates**: To prevent reprocessing messages that have already been handled
-- **Targeting Specific Messages**: When you want to process exactly the messages that match your search criteria
-- **Processing Only New Correspondence**: When you want to handle only new, unread messages directed to you
-
-### Behavior With `--skip-filters` Enabled:
-
-1. The system still uses search to find relevant thread IDs
-2. For each thread found, it fetches ALL messages in that thread
-3. It sorts all messages by timestamp to identify the truly latest message
-4. It processes the latest message in each thread, even if:
- - That message wasn't in the original search results
- - That message was sent by you
- - That message isn't the latest in the original search results
-
-This ensures you're always working with the most current state of each conversation.
-
-## Known Limitations and Troubleshooting
-
-- **Indexing Delays**: The Gmail API's search might miss very recent messages (added in the last few minutes)
-- **Inconsistent Threading**: Gmail's thread IDs are consistent within a session but might change across API calls
-- **Message Visibility**: Some messages might be excluded due to Gmail's categorization (Promotions, Updates, etc.)
-- **Rate Limits**: The Gmail API has rate limits that could affect processing of large email volumes
-
-If messages appear to be missing:
-- Use a larger `--minutes-since` value to cast a wider time net
-- Enable `--include-read` to include messages you've already read
-- Enable `--skip-filters` to process the latest message in each thread
-- Try the combination: `--minutes-since 1440 --include-read --skip-filters`
-
-- **Connection errors:** If you get "Connection refused" or "All connection attempts failed" errors:
- - Make sure the LangGraph server is running with `langgraph start` in a separate terminal
- - Verify the port number matches in your script (default is 2024)
- - Use the `--mock` flag to test without a LangGraph server: `--mock`
-
-## Recent Updates and Fixes
-
-The Gmail integration has been updated with several improvements:
-
-1. **Improved Thread Processing**: Now properly retrieves all messages in a thread, not just the ones found by search
- - Added comprehensive logging of thread messages with dates and senders
- - Fixed sorting to ensure the truly latest message is identified
-
-2. **Enhanced `--skip-filters` Behavior**: When enabled, the system now:
- - Processes the absolute latest message in the thread, even if it wasn't found in search
- - Uses thread-based retrieval to bypass Gmail search limitations
- - Shows detailed information about which messages are being processed
-
-3. **Thread ID Handling**: Improved how thread IDs are mapped between Gmail and LangGraph
- - Uses MD5 hash to ensure consistent ID generation across runs
- - Better error handling for thread ID mapping issues
-
-4. **Simplified Command-Line Interface**:
- - Improved flag handling with boolean flags for better usability
- - Added LangSmith tracing options for better observability
- - Simplified parameters and added clearer documentation
-
-5. **LangSmith Tracing Integration**:
- - Added support for tracing email processing through LangSmith
- - Ensured tracing context is maintained across workflow interrupts
- - Added explicit flags for controlling tracing behavior
-
-- **Authentication issues:** If you encounter a "Token has been expired or revoked" error, delete the existing `token.json` file and run the setup script again to generate a fresh token.
-- **Tracing issues:** If you're not seeing traces in LangSmith after interrupts, ensure you're using the latest version of LangSmith.
-
-## Using Gmail Tools in Your Agent
-
-To use Gmail tools in your agent, modify your agent code as follows:
-
-```python
-from src.email_assistant.tools import get_tools, get_tools_by_name
-from src.email_assistant.tools.gmail.prompt_templates import COMBINED_TOOLS_PROMPT
-
-# Get tools with Gmail integration enabled
-tools = get_tools(include_gmail=True)
-tools_by_name = get_tools_by_name(tools)
-
-# Use the combined tools prompt in your agent's system prompt
-system_prompt = agent_system_prompt.format(
- tools_prompt=COMBINED_TOOLS_PROMPT,
- # other parameters...
-)
-```
-
-See `src/email_assistant/gmail_assistant.py` for a complete example.
diff --git a/python/tools/gmail/__init__.py b/python/tools/gmail/__init__.py
deleted file mode 100644
index 3b239df..0000000
--- a/python/tools/gmail/__init__.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""Gmail tools for email assistant."""
-
-from src.email_assistant.tools.gmail.gmail_tools import (
- fetch_emails_tool,
- send_email_tool,
- check_calendar_tool,
- schedule_meeting_tool
-)
-
-from src.email_assistant.tools.gmail.prompt_templates import GMAIL_TOOLS_PROMPT
-
-__all__ = [
- "fetch_emails_tool",
- "send_email_tool",
- "check_calendar_tool",
- "schedule_meeting_tool",
- "GMAIL_TOOLS_PROMPT"
-]
\ No newline at end of file
diff --git a/python/tools/gmail/gmail_tools.py b/python/tools/gmail/gmail_tools.py
deleted file mode 100644
index a1b223a..0000000
--- a/python/tools/gmail/gmail_tools.py
+++ /dev/null
@@ -1,879 +0,0 @@
-"""
-Gmail tools implementation module.
-This module formats the Gmail API functions into LangChain tools.
-"""
-
-import os
-import sys
-import base64
-import email.utils
-import json
-import logging
-from datetime import datetime
-from typing import List, Optional, Dict, Any, Iterator
-from pathlib import Path
-from pydantic import Field, BaseModel
-from langchain_core.tools import tool
-
-# Setup basic logging
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-# Define paths for credentials and tokens
-_ROOT = Path(__file__).parent.absolute()
-_SECRETS_DIR = _ROOT / ".secrets"
-
-# We need to try importing the Gmail API libraries
-# If they're not available, we'll use a mock implementation
-try:
- import logging
- from googleapiclient.discovery import build
- from email.mime.text import MIMEText
- from datetime import timedelta
- from dateutil.parser import parse as parse_time
- from google.oauth2.credentials import Credentials
- from google_auth_oauthlib.flow import InstalledAppFlow
- from google.auth.transport.requests import Request
-
- # Setup logging
- logging.basicConfig(level=logging.INFO)
- logger = logging.getLogger(__name__)
-
- # Email content extraction function
- def extract_message_part(payload):
- """Extract content from a message part."""
- if payload.get("body", {}).get("data"):
- # Handle base64 encoded content
- data = payload["body"]["data"]
- decoded = base64.urlsafe_b64decode(data).decode("utf-8")
- return decoded
-
- # Handle multipart messages
- if payload.get("parts"):
- text_parts = []
- for part in payload["parts"]:
- # Recursively process parts
- content = extract_message_part(part)
- if content:
- text_parts.append(content)
- return "\n".join(text_parts)
-
- return ""
-
- # Function to get credentials from token.json
- def get_credentials(gmail_token=None, gmail_secret=None):
- """Get Gmail API credentials from token.json"""
- token_path = _SECRETS_DIR / "token.json"
-
- if not os.path.exists(token_path):
- logger.error(f"Token file not found at {token_path}")
- return None
-
- try:
- with open(token_path, "r") as f:
- token_data = json.load(f)
-
- from google.oauth2.credentials import Credentials
-
- # Create credentials object with specific format
- # Using token format from the token.json file
- credentials = Credentials(
- token=token_data.get("token"),
- refresh_token=token_data.get("refresh_token"),
- token_uri=token_data.get("token_uri", "https://oauth2.googleapis.com/token"),
- client_id=token_data.get("client_id"),
- client_secret=token_data.get("client_secret"),
- scopes=token_data.get("scopes", ["https://www.googleapis.com/auth/gmail.modify"])
- )
-
- # Add authorize method to make it compatible with old code
- credentials.authorize = lambda request: request
-
- return credentials
- except Exception as e:
- logger.error(f"Error loading credentials: {str(e)}")
- return None
-
- # Type alias for better readability
- EmailData = Dict[str, Any]
-
- GMAIL_API_AVAILABLE = True
-
-except ImportError:
- # If Gmail API libraries aren't available, set flag to use mock implementation
- GMAIL_API_AVAILABLE = False
- logger = logging.getLogger(__name__)
-
-# Helper function that is used by the tool and can be imported elsewhere
-def fetch_group_emails(
- email_address: str,
- minutes_since: int = 30,
- gmail_token: Optional[str] = None,
- gmail_secret: Optional[str] = None,
- include_read: bool = False,
- skip_filters: bool = False,
-) -> Iterator[Dict[str, Any]]:
- """
- Fetch recent emails from Gmail that involve the specified email address.
-
- This function retrieves emails where the specified address is either a sender
- or recipient, processes them, and returns them in a format suitable for the
- email assistant to process.
-
- Args:
- email_address: Email address to fetch messages for
- minutes_since: Only retrieve emails newer than this many minutes
- gmail_token: Optional token for Gmail API authentication
- gmail_secret: Optional credentials for Gmail API authentication
- include_read: Whether to include already read emails (default: False)
- skip_filters: Skip thread and sender filtering (return all messages, default: False)
-
- Yields:
- Dict objects containing processed email information
- """
- use_mock = False
-
- # Check if we need to use mock implementation
- if not GMAIL_API_AVAILABLE:
- logger.info("Gmail API not available, using mock implementation")
- use_mock = True
-
- # Check if required credential files exist
- if not use_mock and not gmail_token and not gmail_secret:
- token_path = str(_SECRETS_DIR / "token.json")
- secrets_path = str(_SECRETS_DIR / "secrets.json")
-
- if not os.path.exists(token_path) and not os.path.exists(secrets_path):
- logger.warning(f"No Gmail API credentials found. Looking for token.json or secrets.json in .secrets directory")
- logger.warning("Using mock implementation instead")
- use_mock = True
-
- # Return mock data if needed
- if use_mock:
- # For demo purposes, we return a mock email
- mock_email = {
- "from_email": "sender@example.com",
- "to_email": email_address,
- "subject": "Sample Email Subject",
- "page_content": "This is a sample email body for testing the email assistant.",
- "id": "mock-email-id-123",
- "thread_id": "mock-thread-id-123",
- "send_time": datetime.now().isoformat()
- }
-
- yield mock_email
- return
-
- try:
- # Get Gmail API credentials
- creds = get_credentials(gmail_token, gmail_secret)
-
- # Check if credentials are valid
- if not creds or not hasattr(creds, 'authorize'):
- logger.warning("Invalid Gmail credentials, using mock implementation")
- mock_email = {
- "from_email": "sender@example.com",
- "to_email": email_address,
- "subject": "Sample Email Subject - Invalid Credentials",
- "page_content": "This is a mock email because the Gmail credentials are invalid.",
- "id": "mock-email-id-123",
- "thread_id": "mock-thread-id-123",
- "send_time": datetime.now().isoformat()
- }
- yield mock_email
- return
-
- service = build("gmail", "v1", credentials=creds)
-
- # Calculate timestamp for filtering
- after = int((datetime.now() - timedelta(minutes=minutes_since)).timestamp())
-
- # Construct Gmail search query
- # This query searches for:
- # - Emails sent to or from the specified address
- # - Emails after the specified timestamp
- # - Including emails from all categories (inbox, updates, promotions, etc.)
-
- # Base query with time filter
- query = f"(to:{email_address} OR from:{email_address}) after:{after}"
-
- # Only include unread emails unless include_read is True
- if not include_read:
- query += " is:unread"
- else:
- logger.info("Including read emails in search")
-
- # Log the final query for debugging
- logger.info(f"Gmail search query: {query}")
-
- # Additional filter options (commented out by default)
- # If you want to include emails from specific categories, use:
- # query += " category:(primary OR updates OR promotions)"
-
- # Retrieve all matching messages (handling pagination)
- messages = []
- nextPageToken = None
- logger.info(f"Fetching emails for {email_address} from last {minutes_since} minutes")
-
- while True:
- results = (
- service.users()
- .messages()
- .list(userId="me", q=query, pageToken=nextPageToken)
- .execute()
- )
- if "messages" in results:
- new_messages = results["messages"]
- messages.extend(new_messages)
- logger.info(f"Found {len(new_messages)} messages in this page")
- else:
- logger.info("No messages found in this page")
-
- nextPageToken = results.get("nextPageToken")
- if not nextPageToken:
- logger.info(f"Total messages found: {len(messages)}")
- break
-
- # Process each message
- count = 0
- for message in messages:
- try:
- # Get full message details
- msg = service.users().messages().get(userId="me", id=message["id"]).execute()
- thread_id = msg["threadId"]
- payload = msg["payload"]
- headers = payload.get("headers", [])
-
- # Get thread details to determine conversation context
- # Directly fetch the complete thread without any format restriction
- # This matches the exact approach in the test code that successfully gets all messages
- thread = service.users().threads().get(userId="me", id=thread_id).execute()
- messages_in_thread = thread["messages"]
- logger.info(f"Retrieved thread {thread_id} with {len(messages_in_thread)} messages")
-
- # Sort messages by internalDate to ensure proper chronological ordering
- # This ensures we correctly identify the latest message
- if all("internalDate" in msg for msg in messages_in_thread):
- messages_in_thread.sort(key=lambda m: int(m.get("internalDate", 0)))
- logger.info(f"Sorted {len(messages_in_thread)} messages by internalDate")
- else:
- # Fallback to ID-based sorting if internalDate is missing
- messages_in_thread.sort(key=lambda m: m["id"])
- logger.info(f"Sorted {len(messages_in_thread)} messages by ID (internalDate missing)")
-
- # Log details about the messages in the thread for debugging
- for idx, msg in enumerate(messages_in_thread):
- headers = msg["payload"]["headers"]
- subject = next((h["value"] for h in headers if h["name"] == "Subject"), "No Subject")
- from_email = next((h["value"] for h in headers if h["name"] == "From"), "Unknown")
- date = next((h["value"] for h in headers if h["name"] == "Date"), "Unknown")
- logger.info(f" Message {idx+1}/{len(messages_in_thread)}: ID={msg['id']}, Date={date}, From={from_email}")
-
- # Log thread information for debugging
- logger.info(f"Thread {thread_id} has {len(messages_in_thread)} messages")
-
- # Analyze the last message in the thread to determine if we need to process it
- last_message = messages_in_thread[-1]
- last_headers = last_message["payload"]["headers"]
-
- # Get sender of last message
- from_header = next(
- header["value"] for header in last_headers if header["name"] == "From"
- )
- last_from_header = next(
- header["value"]
- for header in last_message["payload"].get("headers")
- if header["name"] == "From"
- )
-
- # If the last message was sent by the user, mark this as a user response
- # and don't process it further (assistant doesn't need to respond to user's own emails)
- if email_address in last_from_header:
- yield {
- "id": message["id"],
- "thread_id": message["threadId"],
- "user_respond": True,
- }
- continue
-
- # Check if this is a message we should process
- is_from_user = email_address in from_header
- is_latest_in_thread = message["id"] == last_message["id"]
-
- # Modified logic for skip_filters:
- # 1. When skip_filters is True, process all messages regardless of position in thread
- # 2. When skip_filters is False, only process if it's not from user AND is latest in thread
- should_process = skip_filters or (not is_from_user and is_latest_in_thread)
-
- if not should_process:
- if is_from_user:
- logger.debug(f"Skipping message {message['id']}: sent by the user")
- elif not is_latest_in_thread:
- logger.debug(f"Skipping message {message['id']}: not the latest in thread")
-
- # Process the message if it passes our filters (or if filters are skipped)
- if should_process:
- # Log detailed information about this message
- logger.info(f"Processing message {message['id']} from thread {thread_id}")
- logger.info(f" Is latest in thread: {is_latest_in_thread}")
- logger.info(f" Skip filters enabled: {skip_filters}")
-
- # If the user wants to process the latest message in the thread,
- # use the last_message from the thread API call instead of the original message
- # that matched the search query
- if not skip_filters:
- # Use original message if skip_filters is False
- process_message = message
- process_payload = payload
- process_headers = headers
- else:
- # Use the latest message in the thread if skip_filters is True
- process_message = last_message
- process_payload = last_message["payload"]
- process_headers = process_payload.get("headers", [])
- logger.info(f"Using latest message in thread: {process_message['id']}")
-
- # Extract email metadata from headers
- subject = next(
- header["value"] for header in process_headers if header["name"] == "Subject"
- )
- from_email = next(
- (header["value"] for header in process_headers if header["name"] == "From"),
- "",
- ).strip()
- _to_email = next(
- (header["value"] for header in process_headers if header["name"] == "To"),
- "",
- ).strip()
-
- # Use Reply-To header if present
- if reply_to := next(
- (
- header["value"]
- for header in process_headers
- if header["name"] == "Reply-To"
- ),
- "",
- ).strip():
- from_email = reply_to
-
- # Extract and parse email timestamp
- send_time = next(
- header["value"] for header in process_headers if header["name"] == "Date"
- )
- parsed_time = parse_time(send_time)
-
- # Extract email body content
- body = extract_message_part(process_payload)
-
- # Yield the processed email data
- yield {
- "from_email": from_email,
- "to_email": _to_email,
- "subject": subject,
- "page_content": body,
- "id": process_message["id"],
- "thread_id": process_message["threadId"],
- "send_time": parsed_time.isoformat(),
- }
- count += 1
-
- except Exception as e:
- logger.warning(f"Failed to process message {message['id']}: {str(e)}")
-
- logger.info(f"Found {count} emails to process out of {len(messages)} total messages.")
-
- except Exception as e:
- logger.error(f"Error accessing Gmail API: {str(e)}")
- # Fall back to mock implementation
- mock_email = {
- "from_email": "sender@example.com",
- "to_email": email_address,
- "subject": "Sample Email Subject",
- "page_content": "This is a sample email body for testing the email assistant.",
- "id": "mock-email-id-123",
- "thread_id": "mock-thread-id-123",
- "send_time": datetime.now().isoformat()
- }
-
- yield mock_email
-
-class FetchEmailsInput(BaseModel):
- """
- Input schema for the fetch_emails_tool.
- """
- email_address: str = Field(
- description="Email address to fetch emails for"
- )
- minutes_since: int = Field(
- default=30,
- description="Only retrieve emails newer than this many minutes"
- )
-
-@tool(args_schema=FetchEmailsInput)
-def fetch_emails_tool(email_address: str, minutes_since: int = 30) -> str:
- """
- Fetches recent emails from Gmail for the specified email address.
-
- Args:
- email_address: Email address to fetch messages for
- minutes_since: Only retrieve emails newer than this many minutes (default: 30)
-
- Returns:
- String summary of fetched emails
- """
- emails = list(fetch_group_emails(email_address, minutes_since))
-
- if not emails:
- return "No new emails found."
-
- result = f"Found {len(emails)} new emails:\n\n"
-
- for i, email in enumerate(emails, 1):
- if email.get("user_respond", False):
- result += f"{i}. You already responded to this email (Thread ID: {email['thread_id']})\n\n"
- continue
-
- result += f"{i}. From: {email['from_email']}\n"
- result += f" To: {email['to_email']}\n"
- result += f" Subject: {email['subject']}\n"
- result += f" Time: {email['send_time']}\n"
- result += f" ID: {email['id']}\n"
- result += f" Thread ID: {email['thread_id']}\n"
- result += f" Content: {email['page_content'][:200]}...\n\n"
-
- return result
-
-class SendEmailInput(BaseModel):
- """
- Input schema for the send_email_tool.
- """
- email_id: str = Field(
- description="Gmail message ID to reply to. This must be a valid Gmail message ID obtained from the fetch_emails_tool. If you're creating a new email (not replying), you can use any string like 'NEW_EMAIL'."
- )
- response_text: str = Field(
- description="Content of the reply"
- )
- email_address: str = Field(
- description="Current user's email address"
- )
- additional_recipients: Optional[List[str]] = Field(
- default=None,
- description="Optional additional recipients to include"
- )
-
-# Helper function for sending emails
-def send_email(
- email_id: str,
- response_text: str,
- email_address: str,
- addn_receipients: Optional[List[str]] = None
-) -> bool:
- """
- Send a reply to an existing email thread or create a new email.
-
- Args:
- email_id: Gmail message ID to reply to. If this is not a valid Gmail ID (e.g., when creating a new email),
- the function will create a new email instead of replying to an existing thread.
- response_text: Content of the reply or new email
- email_address: Current user's email address (the sender)
- addn_receipients: Optional additional recipients
-
- Returns:
- Success flag (True if email was sent)
- """
- if not GMAIL_API_AVAILABLE:
- logger.info("Gmail API not available, simulating email send")
- logger.info(f"Would send: {response_text[:100]}...")
- return True
-
- try:
- # Get Gmail API credentials
- creds = get_credentials()
- service = build("gmail", "v1", credentials=creds)
-
- try:
- # Try to get the original message to extract headers
- message = service.users().messages().get(userId="me", id=email_id).execute()
- headers = message["payload"]["headers"]
-
- # Extract subject with Re: prefix if not already present
- subject = next(header["value"] for header in headers if header["name"] == "Subject")
- if not subject.startswith("Re:"):
- subject = f"Re: {subject}"
-
- # Create a reply message
- original_from = next(header["value"] for header in headers if header["name"] == "From")
-
- # Get thread ID from message
- thread_id = message["threadId"]
- except Exception as e:
- logger.warning(f"Could not retrieve original message with ID {email_id}. Error: {str(e)}")
- # If we can't get the original message, create a new message with minimal info
- subject = "Response"
- original_from = "recipient@example.com" # Will be overridden by user input
- thread_id = None
-
- # Create a message object
- msg = MIMEText(response_text)
- msg["to"] = original_from
- msg["from"] = email_address
- msg["subject"] = subject
-
- # Add additional recipients if specified
- if addn_receipients:
- msg["cc"] = ", ".join(addn_receipients)
-
- # Encode the message
- raw = base64.urlsafe_b64encode(msg.as_bytes()).decode("utf-8")
-
- # Prepare message body
- body = {"raw": raw}
- # Only add threadId if it exists
- if thread_id:
- body["threadId"] = thread_id
-
- # Send the message
- sent_message = (
- service.users()
- .messages()
- .send(
- userId="me",
- body=body,
- )
- .execute()
- )
-
- logger.info(f"Email sent: Message ID {sent_message['id']}")
- return True
-
- except Exception as e:
- logger.error(f"Error sending email: {str(e)}")
- return False
-
-@tool(args_schema=SendEmailInput)
-def send_email_tool(
- email_id: str,
- response_text: str,
- email_address: str,
- additional_recipients: Optional[List[str]] = None
-) -> str:
- """
- Send a reply to an existing email thread or create a new email in Gmail.
-
- Args:
- email_id: Gmail message ID to reply to. This should be a valid Gmail message ID obtained from the fetch_emails_tool.
- If creating a new email rather than replying, you can use any string identifier like "NEW_EMAIL".
- response_text: Content of the reply or new email
- email_address: Current user's email address (the sender)
- additional_recipients: Optional additional recipients to include
-
- Returns:
- Confirmation message
- """
- try:
- success = send_email(
- email_id,
- response_text,
- email_address,
- addn_receipients=additional_recipients
- )
- if success:
- return f"Email reply sent successfully to message ID: {email_id}"
- else:
- return "Failed to send email due to an API error"
- except Exception as e:
- return f"Failed to send email: {str(e)}"
-
-class CheckCalendarInput(BaseModel):
- """
- Input schema for the check_calendar_tool.
- """
- dates: List[str] = Field(
- description="List of dates to check in DD-MM-YYYY format"
- )
-
-def get_calendar_events(dates: List[str]) -> str:
- """
- Check Google Calendar for events on specified dates.
-
- Args:
- dates: List of dates to check in DD-MM-YYYY format
-
- Returns:
- Formatted calendar events for the specified dates
- """
- if not GMAIL_API_AVAILABLE:
- logger.info("Gmail API not available, simulating calendar check")
- result = "Calendar events:\n\n"
- for date in dates:
- result += f"Events for {date}:\n"
- result += " - 9:00 AM - 10:00 AM: Team Meeting\n"
- result += " - 2:00 PM - 3:00 PM: Project Review\n"
- result += "Available slots: 10:00 AM - 2:00 PM, after 3:00 PM\n\n"
- return result
-
- try:
- # Get Gmail API credentials
- creds = get_credentials()
- service = build("calendar", "v3", credentials=creds)
-
- result = "Calendar events:\n\n"
-
- for date_str in dates:
- # Parse date string (DD-MM-YYYY)
- day, month, year = date_str.split("-")
-
- # Format start and end times for the API
- start_time = f"{year}-{month}-{day}T00:00:00Z"
- end_time = f"{year}-{month}-{day}T23:59:59Z"
-
- # Call the Calendar API
- events_result = (
- service.events()
- .list(
- calendarId="primary",
- timeMin=start_time,
- timeMax=end_time,
- singleEvents=True,
- orderBy="startTime",
- )
- .execute()
- )
-
- events = events_result.get("items", [])
-
- result += f"Events for {date_str}:\n"
-
- if not events:
- result += " No events found for this day\n"
- result += " Available all day\n\n"
- continue
-
- # Process events
- busy_slots = []
- for event in events:
- start = event["start"].get("dateTime", event["start"].get("date"))
- end = event["end"].get("dateTime", event["end"].get("date"))
-
- # Convert to datetime objects
- if "T" in start: # dateTime format
- start_dt = datetime.fromisoformat(start.replace("Z", "+00:00"))
- end_dt = datetime.fromisoformat(end.replace("Z", "+00:00"))
-
- # Format for display
- start_display = start_dt.strftime("%I:%M %p")
- end_display = end_dt.strftime("%I:%M %p")
-
- result += f" - {start_display} - {end_display}: {event['summary']}\n"
- busy_slots.append((start_dt, end_dt))
- else: # all-day event
- result += f" - All day: {event['summary']}\n"
- busy_slots.append(("all-day", "all-day"))
-
- # Calculate available slots
- if "all-day" in [slot[0] for slot in busy_slots]:
- result += " Available: No availability (all-day events)\n\n"
- else:
- # Sort busy slots by start time
- busy_slots.sort(key=lambda x: x[0])
-
- # Define working hours (9 AM to 5 PM)
- work_start = datetime(
- year=int(year),
- month=int(month),
- day=int(day),
- hour=9,
- minute=0
- )
- work_end = datetime(
- year=int(year),
- month=int(month),
- day=int(day),
- hour=17,
- minute=0
- )
-
- # Calculate available slots
- available_slots = []
- current = work_start
-
- for start, end in busy_slots:
- if current < start:
- available_slots.append((current, start))
- current = max(current, end)
-
- if current < work_end:
- available_slots.append((current, work_end))
-
- # Format available slots
- if available_slots:
- result += " Available: "
- for i, (start, end) in enumerate(available_slots):
- start_display = start.strftime("%I:%M %p")
- end_display = end.strftime("%I:%M %p")
- result += f"{start_display} - {end_display}"
- if i < len(available_slots) - 1:
- result += ", "
- result += "\n\n"
- else:
- result += " Available: No availability during working hours\n\n"
-
- return result
-
- except Exception as e:
- logger.error(f"Error checking calendar: {str(e)}")
- # Return mock data in case of error
- result = "Calendar events (mock due to error):\n\n"
- for date in dates:
- result += f"Events for {date}:\n"
- result += " - 9:00 AM - 10:00 AM: Team Meeting\n"
- result += " - 2:00 PM - 3:00 PM: Project Review\n"
- result += "Available slots: 10:00 AM - 2:00 PM, after 3:00 PM\n\n"
- return result
-
-@tool(args_schema=CheckCalendarInput)
-def check_calendar_tool(dates: List[str]) -> str:
- """
- Check Google Calendar for events on specified dates.
-
- Args:
- dates: List of dates to check in DD-MM-YYYY format
-
- Returns:
- Formatted calendar events for the specified dates
- """
- try:
- events = get_calendar_events(dates)
- return events
- except Exception as e:
- return f"Failed to check calendar: {str(e)}"
-
-class ScheduleMeetingInput(BaseModel):
- """
- Input schema for the schedule_meeting_tool.
- """
- attendees: List[str] = Field(
- description="Email addresses of meeting attendees"
- )
- title: str = Field(
- description="Meeting title/subject"
- )
- start_time: str = Field(
- description="Meeting start time in ISO format (YYYY-MM-DDTHH:MM:SS)"
- )
- end_time: str = Field(
- description="Meeting end time in ISO format (YYYY-MM-DDTHH:MM:SS)"
- )
- organizer_email: str = Field(
- description="Email address of the meeting organizer"
- )
- timezone: str = Field(
- default="America/Los_Angeles",
- description="Timezone for the meeting"
- )
-
-def send_calendar_invite(
- attendees: List[str],
- title: str,
- start_time: str,
- end_time: str,
- organizer_email: str,
- timezone: str = "America/Los_Angeles"
-) -> bool:
- """
- Schedule a meeting with Google Calendar and send invites.
-
- Args:
- attendees: Email addresses of meeting attendees
- title: Meeting title/subject
- start_time: Meeting start time in ISO format (YYYY-MM-DDTHH:MM:SS)
- end_time: Meeting end time in ISO format (YYYY-MM-DDTHH:MM:SS)
- organizer_email: Email address of the meeting organizer
- timezone: Timezone for the meeting
-
- Returns:
- Success flag (True if meeting was scheduled)
- """
- if not GMAIL_API_AVAILABLE:
- logger.info("Gmail API not available, simulating calendar invite")
- logger.info(f"Would schedule: {title} from {start_time} to {end_time}")
- logger.info(f"Attendees: {', '.join(attendees)}")
- return True
-
- try:
- # Get Gmail API credentials
- creds = get_credentials()
- service = build("calendar", "v3", credentials=creds)
-
- # Create event details
- event = {
- "summary": title,
- "start": {
- "dateTime": start_time,
- "timeZone": timezone,
- },
- "end": {
- "dateTime": end_time,
- "timeZone": timezone,
- },
- "attendees": [{"email": email} for email in attendees],
- "organizer": {
- "email": organizer_email,
- "self": True,
- },
- "reminders": {
- "useDefault": True,
- },
- "sendUpdates": "all", # Send email notifications to attendees
- }
-
- # Create the event
- event = service.events().insert(calendarId="primary", body=event).execute()
-
- logger.info(f"Meeting created: {event.get('htmlLink')}")
- return True
-
- except Exception as e:
- logger.error(f"Error scheduling meeting: {str(e)}")
- return False
-
-@tool(args_schema=ScheduleMeetingInput)
-def schedule_meeting_tool(
- attendees: List[str],
- title: str,
- start_time: str,
- end_time: str,
- organizer_email: str,
- timezone: str = "America/Los_Angeles"
-) -> str:
- """
- Schedule a meeting with Google Calendar and send invites.
-
- Args:
- attendees: Email addresses of meeting attendees
- title: Meeting title/subject
- start_time: Meeting start time in ISO format (YYYY-MM-DDTHH:MM:SS)
- end_time: Meeting end time in ISO format (YYYY-MM-DDTHH:MM:SS)
- organizer_email: Email address of the meeting organizer
- timezone: Timezone for the meeting (default: America/Los_Angeles)
-
- Returns:
- Success or failure message
- """
- try:
- success = send_calendar_invite(
- attendees,
- title,
- start_time,
- end_time,
- organizer_email,
- timezone
- )
-
- if success:
- return f"Meeting '{title}' scheduled successfully from {start_time} to {end_time} with {len(attendees)} attendees"
- else:
- return "Failed to schedule meeting"
- except Exception as e:
- return f"Error scheduling meeting: {str(e)}"
\ No newline at end of file
diff --git a/python/tools/gmail/prompt_templates.py b/python/tools/gmail/prompt_templates.py
deleted file mode 100644
index f8b004c..0000000
--- a/python/tools/gmail/prompt_templates.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""Tool prompt templates for Gmail integration."""
-
-# Gmail tools prompt for insertion into agent system prompts
-GMAIL_TOOLS_PROMPT = """
-1. fetch_emails_tool(email_address, minutes_since) - Fetch recent emails from Gmail
-2. send_email_tool(email_id, response_text, email_address, additional_recipients) - Send a reply to an email thread
-3. check_calendar_tool(dates) - Check Google Calendar availability for specific dates
-4. schedule_meeting_tool(attendees, title, start_time, end_time, organizer_email, timezone) - Schedule a meeting and send invites
-5. triage_email(ignore, notify, respond) - Triage emails into one of three categories
-6. Done - E-mail has been sent
-"""
-
-# Combined tools prompt (default + Gmail) for full integration
-COMBINED_TOOLS_PROMPT = """
-1. fetch_emails_tool(email_address, minutes_since) - Fetch recent emails from Gmail
-2. send_email_tool(email_id, response_text, email_address, additional_recipients) - Send a reply to an email thread
-3. check_calendar_tool(dates) - Check Google Calendar availability for specific dates
-4. schedule_meeting_tool(attendees, title, start_time, end_time, organizer_email, timezone) - Schedule a meeting and send invites
-5. write_email(to, subject, content) - Draft emails to specified recipients
-6. triage_email(ignore, notify, respond) - Triage emails into one of three categories
-7. check_calendar_availability(day) - Check available time slots for a given day
-8. Done - E-mail has been sent
-"""
\ No newline at end of file
diff --git a/python/tools/gmail/run_ingest.py b/python/tools/gmail/run_ingest.py
deleted file mode 100644
index da71a20..0000000
--- a/python/tools/gmail/run_ingest.py
+++ /dev/null
@@ -1,285 +0,0 @@
-#!/usr/bin/env python
-"""
-Simple Gmail ingestion script based directly on test.ipynb that works with LangSmith tracing.
-
-This script provides a minimal implementation for ingesting emails to the LangGraph server,
-with reliable LangSmith tracing.
-"""
-
-import base64
-import json
-import uuid
-import hashlib
-import asyncio
-import argparse
-from pathlib import Path
-from datetime import datetime
-from google.oauth2.credentials import Credentials
-from googleapiclient.discovery import build
-from langgraph_sdk import get_client
-
-# Setup nest_asyncio for asyncio support
-try:
- import nest_asyncio
- nest_asyncio.apply()
- print("Applied nest_asyncio patch")
-except ImportError:
- print("Warning: nest_asyncio not available. Install with: pip install nest_asyncio")
-
-# Setup paths
-_ROOT = Path(__file__).parent.absolute()
-_SECRETS_DIR = _ROOT / ".secrets"
-TOKEN_PATH = _SECRETS_DIR / "token.json"
-
-def extract_message_part(payload):
- """Extract content from a message part."""
- if payload.get("body", {}).get("data"):
- # Handle base64 encoded content
- data = payload["body"]["data"]
- decoded = base64.urlsafe_b64decode(data).decode("utf-8")
- return decoded
-
- # Handle multipart messages
- if payload.get("parts"):
- text_parts = []
- for part in payload["parts"]:
- # Recursively process parts
- content = extract_message_part(part)
- if content:
- text_parts.append(content)
- return "\n".join(text_parts)
-
- return ""
-
-def load_gmail_credentials():
- """Load Gmail credentials from token.json"""
- if not TOKEN_PATH.exists():
- print(f"Error: Token file not found at {TOKEN_PATH}")
- return None
-
- try:
- with open(TOKEN_PATH, "r") as f:
- token_data = json.load(f)
-
- credentials = Credentials(
- token=token_data.get("token"),
- refresh_token=token_data.get("refresh_token"),
- token_uri=token_data.get("token_uri", "https://oauth2.googleapis.com/token"),
- client_id=token_data.get("client_id"),
- client_secret=token_data.get("client_secret"),
- scopes=token_data.get("scopes", ["https://www.googleapis.com/auth/gmail.modify"])
- )
- return credentials
- except Exception as e:
- print(f"Error loading credentials: {str(e)}")
- return None
-
-def extract_email_data(message):
- """Extract key information from a Gmail message."""
- headers = message['payload']['headers']
-
- # Extract key headers
- subject = next((h['value'] for h in headers if h['name'] == 'Subject'), 'No Subject')
- from_email = next((h['value'] for h in headers if h['name'] == 'From'), 'Unknown Sender')
- to_email = next((h['value'] for h in headers if h['name'] == 'To'), 'Unknown Recipient')
- date = next((h['value'] for h in headers if h['name'] == 'Date'), 'Unknown Date')
-
- # Extract message content
- content = extract_message_part(message['payload'])
-
- # Create email data object
- email_data = {
- "from_email": from_email,
- "to_email": to_email,
- "subject": subject,
- "page_content": content,
- "id": message['id'],
- "thread_id": message['threadId'],
- "send_time": date
- }
-
- return email_data
-
-async def ingest_email_to_langgraph(email_data, graph_name, url="http://127.0.0.1:2024"):
- """Ingest an email to LangGraph."""
- # Connect to LangGraph server
- client = get_client(url=url)
-
- # Create a consistent UUID for the thread
- raw_thread_id = email_data["thread_id"]
- thread_id = str(
- uuid.UUID(hex=hashlib.md5(raw_thread_id.encode("UTF-8")).hexdigest())
- )
- print(f"Gmail thread ID: {raw_thread_id} → LangGraph thread ID: {thread_id}")
-
- try:
- # Try to get existing thread info
- thread_info = await client.threads.get(thread_id)
- print(f"Found existing thread: {thread_id}")
- except Exception as e:
- # If thread doesn't exist, create it
- print(f"Creating new thread: {thread_id}")
- thread_info = await client.threads.create(thread_id=thread_id)
-
- # Update thread metadata with current email ID
- await client.threads.update(thread_id, metadata={"email_id": email_data["id"]})
-
- # Create a run for this email
- print(f"Creating run for thread {thread_id} with graph {graph_name}")
-
- run = await client.runs.create(
- thread_id,
- graph_name,
- input={"email_input": {
- "from": email_data["from_email"],
- "to": email_data["to_email"],
- "subject": email_data["subject"],
- "body": email_data["page_content"],
- "id": email_data["id"]
- }},
- multitask_strategy="rollback",
- )
-
- print(f"Run created successfully")
- print(f"View in LangGraph Studio: http://127.0.0.1:2024/threads/{thread_id}")
-
- return thread_id, run
-
-async def fetch_and_process_emails(args):
- """Fetch emails from Gmail and process them through LangGraph."""
- # Load Gmail credentials
- credentials = load_gmail_credentials()
- if not credentials:
- print("Failed to load Gmail credentials")
- return 1
-
- # Build Gmail service
- service = build("gmail", "v1", credentials=credentials)
-
- # Process emails
- processed_count = 0
-
- try:
- # Get messages from the specified email address
- email_address = args.email
-
- # Construct Gmail search query
- query = f"to:{email_address} OR from:{email_address}"
-
- # Add time constraint if specified
- if args.minutes_since > 0:
- # Calculate timestamp for filtering
- from datetime import timedelta
- after = int((datetime.now() - timedelta(minutes=args.minutes_since)).timestamp())
- query += f" after:{after}"
-
- # Only include unread emails unless include_read is True
- if not args.include_read:
- query += " is:unread"
-
- print(f"Gmail search query: {query}")
-
- # Execute the search
- results = service.users().messages().list(userId="me", q=query).execute()
- messages = results.get("messages", [])
-
- if not messages:
- print("No emails found matching the criteria")
- return 0
-
- print(f"Found {len(messages)} emails")
-
- # Process each email
- for i, message_info in enumerate(messages):
- # Stop early if requested
- if args.early and i > 0:
- print(f"Early stop after processing {i} emails")
- break
-
- # Check if we should reprocess this email
- if not args.rerun:
- # TODO: Add check for already processed emails
- pass
-
- # Get the full message
- message = service.users().messages().get(userId="me", id=message_info["id"]).execute()
-
- # Extract email data
- email_data = extract_email_data(message)
-
- print(f"\nProcessing email {i+1}/{len(messages)}:")
- print(f"From: {email_data['from_email']}")
- print(f"Subject: {email_data['subject']}")
-
- # Ingest to LangGraph
- thread_id, run = await ingest_email_to_langgraph(
- email_data,
- args.graph_name,
- url=args.url
- )
-
- processed_count += 1
-
- print(f"\nProcessed {processed_count} emails successfully")
- return 0
-
- except Exception as e:
- print(f"Error processing emails: {str(e)}")
- return 1
-
-def parse_args():
- """Parse command line arguments."""
- parser = argparse.ArgumentParser(description="Simple Gmail ingestion for LangGraph with reliable tracing")
-
- parser.add_argument(
- "--email",
- type=str,
- required=True,
- help="Email address to fetch messages for"
- )
- parser.add_argument(
- "--minutes-since",
- type=int,
- default=120,
- help="Only retrieve emails newer than this many minutes"
- )
- parser.add_argument(
- "--graph-name",
- type=str,
- default="email_assistant_hitl_memory",
- help="Name of the LangGraph to use"
- )
- parser.add_argument(
- "--url",
- type=str,
- default="http://127.0.0.1:2024",
- help="URL of the LangGraph deployment"
- )
- parser.add_argument(
- "--early",
- action="store_true",
- help="Early stop after processing one email"
- )
- parser.add_argument(
- "--include-read",
- action="store_true",
- help="Include emails that have already been read"
- )
- parser.add_argument(
- "--rerun",
- action="store_true",
- help="Process the same emails again even if already processed"
- )
- parser.add_argument(
- "--skip-filters",
- action="store_true",
- help="Skip filtering of emails"
- )
- return parser.parse_args()
-
-if __name__ == "__main__":
- # Get command line arguments
- args = parse_args()
-
- # Run the script
- exit(asyncio.run(fetch_and_process_emails(args)))
\ No newline at end of file
diff --git a/python/tools/gmail/setup_gmail.py b/python/tools/gmail/setup_gmail.py
deleted file mode 100644
index a5a1a96..0000000
--- a/python/tools/gmail/setup_gmail.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/env python
-"""
-Setup script for Gmail API integration.
-
-This script handles the OAuth flow for Gmail API access by:
-1. Creating a .secrets directory if it doesn't exist
-2. Using credentials from .secrets/secrets.json to authenticate
-3. Opening a browser window for user authentication
-4. Storing the access token in .secrets/token.json
-"""
-
-import os
-import sys
-import json
-from pathlib import Path
-
-# Add project root to sys.path for imports to work correctly
-sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../")))
-
-# Import required Google libraries
-from google_auth_oauthlib.flow import InstalledAppFlow
-from google.oauth2.credentials import Credentials
-
-def main():
- """Run Gmail authentication setup."""
- # Create .secrets directory
- secrets_dir = Path(__file__).parent.absolute() / ".secrets"
- secrets_dir.mkdir(parents=True, exist_ok=True)
-
- # Check for secrets.json
- secrets_path = secrets_dir / "secrets.json"
- if not secrets_path.exists():
- print(f"Error: Client secrets file not found at {secrets_path}")
- print("Please download your OAuth client ID JSON from Google Cloud Console")
- print("and save it as .secrets/secrets.json")
- return 1
-
- print("Starting Gmail API authentication flow...")
- print("A browser window will open for you to authorize access.")
-
- # This will trigger the OAuth flow and create token.json
- try:
- # Define the scopes we need
- SCOPES = [
- 'https://www.googleapis.com/auth/gmail.modify',
- 'https://www.googleapis.com/auth/calendar'
- ]
-
- # Load client secrets
- with open(secrets_path, 'r') as f:
- client_config = json.load(f)
-
- # Create the flow using the client_secrets.json format
- flow = InstalledAppFlow.from_client_secrets_file(
- str(secrets_path),
- SCOPES
- )
-
- # Run the OAuth flow
- credentials = flow.run_local_server(port=0)
-
- # Save the credentials to token.json
- token_path = secrets_dir / "token.json"
- token_data = {
- 'token': credentials.token,
- 'refresh_token': credentials.refresh_token,
- 'token_uri': credentials.token_uri,
- 'client_id': credentials.client_id,
- 'client_secret': credentials.client_secret,
- 'scopes': credentials.scopes,
- 'universe_domain': 'googleapis.com',
- 'account': '',
- 'expiry': credentials.expiry.isoformat() + "Z"
- }
-
- with open(token_path, 'w') as token_file:
- json.dump(token_data, token_file)
-
- print("\nAuthentication successful!")
- print(f"Access token stored at {token_path}")
- return 0
- except Exception as e:
- print(f"Authentication failed: {str(e)}")
- return 1
-
-if __name__ == "__main__":
- exit(main())
\ No newline at end of file
diff --git a/python/utils.py b/python/utils.py
deleted file mode 100644
index 0a6332e..0000000
--- a/python/utils.py
+++ /dev/null
@@ -1,243 +0,0 @@
-from typing import List, Any
-import io
-import sys
-import json
-
-def format_email_markdown(subject, author, to, email_thread):
- """Format email details into a nicely formatted markdown string for display"""
- return f"""
-
-**Subject**: {subject}
-**From**: {author}
-**To**: {to}
-
-{email_thread}
-
----
-"""
-
-def format_for_display(state, tool_call):
- """Format content for display in Agent Inbox
-
- Args:
- state: Current message state
- tool_call: The tool call to format
- """
- # Initialize empty display
- display = ""
-
- # Add tool call information
- if tool_call["name"] == "write_email":
- display += f"""# Email Draft
-
-**To**: {tool_call["args"].get("to")}
-**Subject**: {tool_call["args"].get("subject")}
-
-{tool_call["args"].get("content")}
-"""
- elif tool_call["name"] == "schedule_meeting":
- display += f"""# Calendar Invite
-
-**Meeting**: {tool_call["args"].get("subject")}
-**Attendees**: {', '.join(tool_call["args"].get("attendees"))}
-**Duration**: {tool_call["args"].get("duration_minutes")} minutes
-**Day**: {tool_call["args"].get("preferred_day")}
-"""
- elif tool_call["name"] == "Question":
- # Special formatting for questions to make them clear
- display += f"""# Question for User
-
-{tool_call["args"].get("content")}
-"""
- else:
- # Generic format for other tools
- display += f"""# Tool Call: {tool_call["name"]}
-
-Arguments:"""
-
- # Check if args is a dictionary or string
- if isinstance(tool_call["args"], dict):
- display += f"\n{json.dumps(tool_call['args'], indent=2)}\n"
- else:
- display += f"\n{tool_call['args']}\n"
- return display
-
-def parse_email(email_input: dict) -> tuple[str, str, str, str]:
- """Parse an email input dictionary, supporting multiple schemas.
-
- Supports multiple schema formats:
- - Standard schema (author, to, subject, email_thread)
- - Gmail-specific schema (from_email, to_email, subject, page_content)
- - Direct API schema (from, to, subject, body)
-
- Args:
- email_input (dict): Dictionary containing email fields in any of these formats:
- Standard schema:
- - author: Sender's name and email
- - to: Recipient's name and email
- - subject: Email subject line
- - email_thread: Full email content
- Gmail schema:
- - from_email: Sender's email
- - to_email: Recipient's email
- - subject: Email subject line
- - page_content: Full email content
- - id: Gmail message ID
- - thread_id: Gmail thread ID
- - send_time: Time the email was sent
- Direct API schema:
- - from: Sender's email
- - to: Recipient's email
- - subject: Email subject line
- - body: Full email content
-
- Returns:
- tuple[str, str, str, str]: Tuple containing:
- - author: Sender's name and email
- - to: Recipient's name and email
- - subject: Email subject line
- - email_thread: Full email content
- """
- # Detect schema based on keys present in the input
- if "author" in email_input and "email_thread" in email_input:
- # Standard schema
- return (
- email_input["author"],
- email_input["to"],
- email_input["subject"],
- email_input["email_thread"],
- )
- elif "from_email" in email_input and "page_content" in email_input:
- # Gmail schema
- return (
- email_input["from_email"],
- email_input["to_email"],
- email_input["subject"],
- email_input["page_content"],
- )
- elif "from" in email_input and "body" in email_input:
- # Direct API schema
- return (
- email_input["from"],
- email_input["to"],
- email_input["subject"],
- email_input["body"],
- )
- else:
- # Unknown schema, try to handle gracefully by looking for equivalent fields
- author = (
- email_input.get("author") or
- email_input.get("from_email") or
- email_input.get("from") or
- "Unknown Sender"
- )
- to = (
- email_input.get("to") or
- email_input.get("to_email") or
- "Unknown Recipient"
- )
- subject = email_input.get("subject") or "No Subject"
- content = (
- email_input.get("email_thread") or
- email_input.get("page_content") or
- email_input.get("body") or
- email_input.get("content") or
- "No content available"
- )
- return (author, to, subject, content)
-
-def extract_message_content(message) -> str:
- """Extract content from different message types as clean string.
-
- Args:
- message: A message object (HumanMessage, AIMessage, ToolMessage)
-
- Returns:
- str: Extracted content as clean string
- """
- content = message.content
-
- # Check for recursion marker in string
- if isinstance(content, str) and ' List[str]:
- """Extract tool call names from messages, safely handling messages without tool_calls."""
- tool_call_names = []
- for message in messages:
- # Check if message is a dict and has tool_calls
- if isinstance(message, dict) and message.get("tool_calls"):
- tool_call_names.extend([call["name"].lower() for call in message["tool_calls"]])
- # Check if message is an object with tool_calls attribute
- elif hasattr(message, "tool_calls") and message.tool_calls:
- tool_call_names.extend([call["name"].lower() for call in message.tool_calls])
-
- return tool_call_names
-
-def format_messages_string(messages: List[Any]) -> str:
- """Format messages into a single string for analysis."""
- # Redirect stdout to capture output
- old_stdout = sys.stdout
- new_stdout = io.StringIO()
- sys.stdout = new_stdout
-
- # Run the pretty_print calls
- for m in messages:
- m.pretty_print()
-
- # Get the captured output
- output = new_stdout.getvalue()
-
- # Restore original stdout
- sys.stdout = old_stdout
-
- return output
\ No newline at end of file
diff --git a/scripts/dev.ts b/scripts/dev.ts
new file mode 100644
index 0000000..c5cb7b4
--- /dev/null
+++ b/scripts/dev.ts
@@ -0,0 +1,35 @@
+import { getEmailAssistant } from './email_assistant.js';
+import { EmailData } from '../lib/schemas.js';
+
+async function main() {
+ console.log('Starting email assistant...');
+
+ try {
+ // Initialize the email assistant
+ const agent = await getEmailAssistant();
+
+ // Example email data
+ const exampleEmail: EmailData = {
+ id: "123456",
+ thread_id: "thread_123456",
+ from_email: "example@example.com",
+ to_email: "assistant@yourcompany.com",
+ subject: "Meeting Request Tomorrow",
+ page_content: "Hi there,\n\nI was wondering if we could schedule a meeting tomorrow to discuss the project.\n\nThanks,\nExample User",
+ send_time: new Date().toISOString()
+ };
+
+ // Invoke the agent with example data
+ console.log('Processing email...');
+ const result = await agent.invoke({
+ email_input: exampleEmail
+ });
+
+ console.log('Processing complete!');
+ console.log('Result:', JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error('Error running email assistant:', error);
+ }
+}
+
+main().catch(console.error);
\ No newline at end of file
diff --git a/app/page.tsx b/scripts/email_assistant.ts
similarity index 89%
rename from app/page.tsx
rename to scripts/email_assistant.ts
index a21bd7b..d8127a1 100644
--- a/app/page.tsx
+++ b/scripts/email_assistant.ts
@@ -1,4 +1,3 @@
-import Image from "next/image";
// LangChain imports for chat models
import { initChatModel } from "langchain/chat_models/universal";
@@ -27,7 +26,7 @@ import { ToolNode } from "@langchain/langgraph/prebuilt";
import {
getTools,
getToolsByName
-} from "../lib/tools/base";
+} from "../lib/tools/base.js";
import {
agentSystemPromptBaseline,
triageSystemPrompt,
@@ -40,40 +39,19 @@ import {
defaultCalPreferences,
defaultTriageInstructions,
AGENT_TOOLS_PROMPT
-} from "../lib/prompts";
+} from "../lib/prompts.js";
import {
RouterSchema,
RouterOutput,
EmailData,
StateInput,
State,
-} from "../lib/schemas";
+} from "../lib/schemas.js";
import {
parseEmail,
formatEmailMarkdown
-} from "../lib/utils";
+} from "../lib/utils.js";
-export default function Home() {
- return (
-
-
Hello World
-
-
-
Email Assistant
-
This page contains the TypeScript implementation of the email assistant workflow.
-
-
-
-
- );
-}
// Create the email assistant workflow
export const createEmailAssistant = async () => {
@@ -140,7 +118,7 @@ export const createEmailAssistant = async () => {
if (isAIMessage(lastMessage) && lastMessage.tool_calls && lastMessage.tool_calls.length > 0) {
// Check if any tool call is the "Done" tool
- if (lastMessage.tool_calls.some(toolCall => toolCall.name === "Done")) {
+ if (lastMessage.tool_calls.some((toolCall: ToolCall) => toolCall.name === "Done")) {
return END;
}
return "environment";
diff --git a/app/email_assistant_hitl/page.tsx b/scripts/email_assistant_hitl.ts
similarity index 99%
rename from app/email_assistant_hitl/page.tsx
rename to scripts/email_assistant_hitl.ts
index 51b9e9d..da78a47 100644
--- a/app/email_assistant_hitl/page.tsx
+++ b/scripts/email_assistant_hitl.ts
@@ -26,7 +26,7 @@ import { ToolNode } from "@langchain/langgraph/prebuilt";
import {
getTools,
getToolsByName
-} from "../../lib/tools/base";
+} from "../lib/tools/base.js";
import {
HITL_TOOLS_PROMPT,
triageSystemPrompt,
@@ -36,19 +36,19 @@ import {
defaultResponsePreferences,
defaultCalPreferences,
defaultTriageInstructions
-} from "../../lib/prompts";
+} from "../lib/prompts.js";
import {
RouterSchema,
RouterOutput,
EmailData,
StateInput,
State,
-} from "../../lib/schemas";
+} from "../lib/schemas.js";
import {
parseEmail,
formatEmailMarkdown,
formatForDisplay
-} from "../../lib/utils";
+} from "../lib/utils.js";
/**
* Create the Human-in-the-Loop Email Assistant
diff --git a/tsconfig.json b/tsconfig.json
index d8b9323..90a2861 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,27 +1,22 @@
{
"compilerOptions": {
- "target": "ES2017",
- "lib": ["dom", "dom.iterable", "esnext"],
+ "target": "ES2020",
+ "lib": ["ES2020", "DOM"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
- "noEmit": true,
+ "forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
"resolveJsonModule": true,
"isolatedModules": true,
- "jsx": "preserve",
- "incremental": true,
- "plugins": [
- {
- "name": "next"
- }
- ],
+ "baseUrl": ".",
"paths": {
"@/*": ["./*"]
- }
+ },
+ "outDir": "dist"
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
+ "include": ["**/*.ts"],
+ "exclude": ["node_modules", "dist"]
}