From c940486a06b5d6b426d2f1a9b7aaeca0094cbaf9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:12:12 -0400 Subject: [PATCH 1/6] =?UTF-8?q?=F0=9F=8C=8D=20i18n:=20Update=20translation?= =?UTF-8?q?.json=20with=20latest=20translations=20(#12571)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- client/src/locales/lv/translation.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/locales/lv/translation.json b/client/src/locales/lv/translation.json index 5bce8e3406..79e37b3eba 100644 --- a/client/src/locales/lv/translation.json +++ b/client/src/locales/lv/translation.json @@ -400,7 +400,7 @@ "com_info_heic_converting": "Konvertēju HEIC attēlu uz JPEG...", "com_nav_2fa": "Divfaktoru autentifikācija (2FA)", "com_nav_account_settings": "Konta iestatījumi", - "com_nav_always_make_prod": "Vienmēr uzlieciet jaunas versijas produkcijā", + "com_nav_always_make_prod": "Vienmēr producēt jaunāko uzvednes versiju", "com_nav_archive_created_at": "Arhivēšanas datums", "com_nav_archive_name": "Vārds", "com_nav_archived_chats": "Arhivētās sarunas", @@ -410,7 +410,7 @@ "com_nav_audio_process_error": "Kļūda, apstrādājot audio: {{0}}", "com_nav_auto_expand_tools": "Automātiski paplašināt rīka informāciju", "com_nav_auto_scroll": "Automātiski iet uz jaunāko ziņu, atverot sarunu", - "com_nav_auto_send_prompts": "Automātiski sūtīt uzvednes", + "com_nav_auto_send_prompts": "Atlasot uzvedi, nosūtīt to automātiski uzreiz", "com_nav_auto_send_prompts_desc": "Automātiski iesniegt uzaicinājumu sarunai, kad tas ir atlasīts", "com_nav_auto_send_text": "Automātiski nosūtīt tekstu", "com_nav_auto_transcribe_audio": "Automātiski transkribēt audio", @@ -1056,7 +1056,7 @@ "com_ui_generating_image": "Attēla ģenerēšana...", "com_ui_generation_settings": "Ģenerēšanas iestatījumi", "com_ui_getting_started": "Darba sākšana", - "com_ui_global_group": "Nav rezultātu", + "com_ui_global_group": "Globālā uzvedne", "com_ui_go_back": "Atgriezties", "com_ui_go_to_conversation": "Doties uz sarunu", "com_ui_good_afternoon": "Labdien", @@ -1445,7 +1445,7 @@ "com_ui_special_var_desc_iso_datetime": "UTC datuma laiks ISO 8601 formātā", "com_ui_special_var_iso_datetime": "UTC ISO datums un laiks", "com_ui_special_variable_added": "{{0}} pievienots īpašs mainīgais.", - "com_ui_special_variables": "Īpašie mainīgie:", + "com_ui_special_variables": "Īpašie mainīgie", "com_ui_speech_not_supported": "Jūsu pārlūkprogramma neatbalsta runas atpazīšanu", "com_ui_speech_not_supported_use_external": "Jūsu pārlūkprogramma neatbalsta runas atpazīšanu. Mēģiniet pārslēgties uz ārējo STT sadaļā Iestatījumi > Runa.", "com_ui_speech_while_submitting": "Nevar nosūtīt runu, kamēr tiek ģenerēta atbilde.", From 622a934a82ebf427d111922bd7c0c1560edb5730 Mon Sep 17 00:00:00 2001 From: neuralnotesyt-netizen Date: Wed, 8 Apr 2026 18:42:44 +0530 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=97=92=EF=B8=8F=20docs:=20Update=20LI?= =?UTF-8?q?CENSE.md=20Year:=202025=20->=202026=20(#12554)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 535850a920..5ee6463dee 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 LibreChat +Copyright (c) 2026 LibreChat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 96312aa4fd2425a5e3d0a8c75bd635957b4140ce Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Wed, 8 Apr 2026 15:07:38 -0400 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=8E=AF=20fix:=20Use=20Resolved=20Prov?= =?UTF-8?q?ider=20for=20Agent=20Token=20Lookup=20on=20Custom=20Endpoints?= =?UTF-8?q?=20(#12574)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: Use resolved provider for agent token lookup on custom endpoints The providerEndpointMap lookup in initializeAgent used the original provider name (e.g. "EduGPT") instead of the resolved overrideProvider ("openai"). Since providerEndpointMap only contains 4 built-in providers, custom providers resolved to undefined, causing getModelMaxTokens to miss the token map and fall back to 18000 tokens. With agent instructions + tool schemas consuming most of that budget, createPruneMessages would strip all messages on the first turn. * fix: Use correct EndpointTokenConfig type in test * refactor: Unify test factory, remove non-discriminating test Address review findings: - Remove Test 2 ("uses the model real context window") which passed with and without the fix due to getModelMaxTokens defaulting to openAI when endpoint is undefined (JS default parameter semantics) - Merge createCustomProviderMocks into createMocks via provider, overrideProvider, and useRealTokenLookup parameters - Hoist jest.requireActual to file scope for shared access * refactor: Address followup review findings - Replace loose `maxContextTokens > 18000` assertion with precise computed value `Math.round((65536 - 4096) * 0.95)` so the outcome assertion is meaningful and self-documenting - Hoist `customProvider` to describe-level constant `CUSTOM_PROVIDER` - Document `overrideProvider` semantics and `useRealTokenLookup` in factory JSDoc - Add comment on real `optionalChainWithEmptyCheck` noting its zero-handling semantics are load-bearing for the maxContextTokens=0 test * style: Use // for inline comment, clarify pipeline assertion role --- .../src/agents/__tests__/initialize.test.ts | 142 ++++++++++++++---- packages/api/src/agents/initialize.ts | 2 +- 2 files changed, 117 insertions(+), 27 deletions(-) diff --git a/packages/api/src/agents/__tests__/initialize.test.ts b/packages/api/src/agents/__tests__/initialize.test.ts index f9982a6e46..fe1493628a 100644 --- a/packages/api/src/agents/__tests__/initialize.test.ts +++ b/packages/api/src/agents/__tests__/initialize.test.ts @@ -1,7 +1,7 @@ import { Providers } from '@librechat/agents'; import { EModelEndpoint } from 'librechat-data-provider'; import type { Agent } from 'librechat-data-provider'; -import type { ServerRequest, InitializeResultBase } from '~/types'; +import type { ServerRequest, InitializeResultBase, EndpointTokenConfig } from '~/types'; import type { InitializeAgentDbMethods } from '../initialize'; // Mock logger @@ -55,22 +55,48 @@ jest.mock('../resources', () => ({ import { initializeAgent } from '../initialize'; +const realUtils = jest.requireActual('~/utils'); + /** * Creates minimal mock objects for initializeAgent tests. + * + * @param overrides.overrideProvider - Simulates the value returned by `getProviderConfig`. + * Defaults to `provider` (native endpoint where no remapping occurs). Set to a different + * value (e.g. `Providers.OPENAI`) alongside a custom `provider` to simulate a custom + * endpoint whose provider is resolved to a built-in. + * @param overrides.useRealTokenLookup - When true, `getModelMaxTokens` delegates to the real + * implementation so tests exercise actual token-map resolution. Otherwise a controlled + * `modelDefault` is returned. */ function createMocks(overrides?: { + provider?: string; + overrideProvider?: string; + model?: string; maxContextTokens?: number; modelDefault?: number; maxOutputTokens?: number; + endpointTokenConfig?: EndpointTokenConfig; + useRealTokenLookup?: boolean; }) { - const { maxContextTokens, modelDefault = 200000, maxOutputTokens = 4096 } = overrides ?? {}; + const { + provider = Providers.OPENAI, + overrideProvider, + model = 'test-model', + maxContextTokens, + modelDefault = 200000, + maxOutputTokens = 4096, + endpointTokenConfig, + useRealTokenLookup = false, + } = overrides ?? {}; + + const resolvedOverrideProvider = overrideProvider ?? provider; const agent = { id: 'agent-1', - model: 'test-model', - provider: Providers.OPENAI, + model, + provider, tools: [], - model_parameters: { model: 'test-model' }, + model_parameters: { model }, } as unknown as Agent; const req = { @@ -81,39 +107,29 @@ function createMocks(overrides?: { const res = {} as unknown as import('express').Response; const mockGetOptions = jest.fn().mockResolvedValue({ - llmConfig: { - model: 'test-model', - maxTokens: maxOutputTokens, - }, - endpointTokenConfig: undefined, + llmConfig: { model, maxTokens: maxOutputTokens }, + endpointTokenConfig, } satisfies InitializeResultBase); mockGetProviderConfig.mockReturnValue({ getOptions: mockGetOptions, - overrideProvider: Providers.OPENAI, + overrideProvider: resolvedOverrideProvider, }); - // extractLibreChatParams returns maxContextTokens when provided in model_parameters mockExtractLibreChatParams.mockReturnValue({ resendFiles: false, maxContextTokens, - modelOptions: { model: 'test-model' }, + modelOptions: { model }, }); - // getModelMaxTokens returns the model's default context window - mockGetModelMaxTokens.mockReturnValue(modelDefault); + if (useRealTokenLookup) { + mockGetModelMaxTokens.mockImplementation(realUtils.getModelMaxTokens); + } else { + mockGetModelMaxTokens.mockReturnValue(modelDefault); + } - // Implement real optionalChainWithEmptyCheck behavior - mockOptionalChainWithEmptyCheck.mockImplementation( - (...values: (string | number | undefined)[]) => { - for (const v of values) { - if (v !== undefined && v !== null && v !== '') { - return v; - } - } - return values[values.length - 1]; - }, - ); + // Real implementation: treats 0 as a valid (non-empty) value — load-bearing for the maxContextTokens=0 test + mockOptionalChainWithEmptyCheck.mockImplementation(realUtils.optionalChainWithEmptyCheck); const loadTools = jest.fn().mockResolvedValue({ tools: [], @@ -136,6 +152,80 @@ function createMocks(overrides?: { return { agent, req, res, loadTools, db }; } +describe('initializeAgent — custom provider token lookup', () => { + const CUSTOM_PROVIDER = 'EduGPT'; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('passes the resolved provider endpoint to getModelMaxTokens, not the custom name', async () => { + const { agent, req, res, loadTools, db } = createMocks({ + provider: CUSTOM_PROVIDER, + overrideProvider: Providers.OPENAI, + model: 'qwen3-235b-a22b', + useRealTokenLookup: true, + }); + + await initializeAgent( + { + req, + res, + agent, + loadTools, + endpointOption: { endpoint: EModelEndpoint.agents }, + allowedProviders: new Set([CUSTOM_PROVIDER]), + isInitialAgent: true, + }, + db, + ); + + // providerEndpointMap["openAI"] = "openAI" (valid), not providerEndpointMap["EduGPT"] = undefined + expect(mockGetModelMaxTokens).toHaveBeenCalledWith( + 'qwen3-235b-a22b', + EModelEndpoint.openAI, + undefined, + ); + }); + + it('uses endpointTokenConfig from the custom endpoint for unrecognized models', async () => { + const customTokenConfig: EndpointTokenConfig = { + 'my-custom-model-v1': { context: 65536, prompt: 1, completion: 1 }, + }; + const { agent, req, res, loadTools, db } = createMocks({ + provider: CUSTOM_PROVIDER, + overrideProvider: Providers.OPENAI, + model: 'my-custom-model-v1', + endpointTokenConfig: customTokenConfig, + useRealTokenLookup: true, + }); + + const result = await initializeAgent( + { + req, + res, + agent, + loadTools, + endpointOption: { endpoint: EModelEndpoint.agents }, + allowedProviders: new Set([CUSTOM_PROVIDER]), + isInitialAgent: true, + }, + db, + ); + + expect(mockGetModelMaxTokens).toHaveBeenCalledWith( + 'my-custom-model-v1', + EModelEndpoint.openAI, + customTokenConfig, + ); + + // Pipeline check: verifies endpointTokenConfig.context flows through the full + // optionalChainWithEmptyCheck → Math.max formula. The toHaveBeenCalledWith + // assertion above catches the actual provider-resolution regression. + expect(result.maxContextTokens).toBe(Math.round((65536 - 4096) * 0.95)); + }); +}); + describe('initializeAgent — maxContextTokens', () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/api/src/agents/initialize.ts b/packages/api/src/agents/initialize.ts index 81bc89cac4..1ebd20bb01 100644 --- a/packages/api/src/agents/initialize.ts +++ b/packages/api/src/agents/initialize.ts @@ -354,7 +354,7 @@ export async function initializeAgent( maxContextTokens, getModelMaxTokens( tokensModel ?? '', - providerEndpointMap[provider as keyof typeof providerEndpointMap], + providerEndpointMap[overrideProvider as keyof typeof providerEndpointMap], options.endpointTokenConfig, ), 18000, From 01a1bc1689be74792c0f3d13711ff13d48c3f04e Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Wed, 8 Apr 2026 16:17:19 -0400 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=93=8A=20experimental:=20Add=20GitNex?= =?UTF-8?q?us=20CI/CD=20and=20deployment=20configuration=20(#12577)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add GitNexus CI/CD and deployment configuration - Introduced a Dockerfile for building the GitNexus application with necessary dependencies and configurations. - Added a Caddyfile to set up a reverse proxy with bearer token authentication for secure access to GitNexus. - Created an entrypoint script to validate the API token and start both GitNexus and Caddy services. - Configured Fly.io deployment settings in fly.toml, including health checks and service parameters. - Established GitHub Actions workflows for deploying the GitNexus index and managing deployments to Fly.io. * fix: use npx instead of bunx for native addon compatibility bunx skips node-gyp lifecycle scripts, so @ladybugdb/core's native .node binary never gets compiled/downloaded. npx handles this correctly. --- .fly/gitnexus/Caddyfile | 21 ++++++ .fly/gitnexus/Dockerfile | 29 +++++++ .fly/gitnexus/entrypoint.sh | 14 ++++ .fly/gitnexus/fly.toml | 24 ++++++ .github/workflows/gitnexus-deploy.yml | 104 ++++++++++++++++++++++++++ .github/workflows/gitnexus-index.yml | 91 ++++++++++++++++++++++ 6 files changed, 283 insertions(+) create mode 100644 .fly/gitnexus/Caddyfile create mode 100644 .fly/gitnexus/Dockerfile create mode 100644 .fly/gitnexus/entrypoint.sh create mode 100644 .fly/gitnexus/fly.toml create mode 100644 .github/workflows/gitnexus-deploy.yml create mode 100644 .github/workflows/gitnexus-index.yml diff --git a/.fly/gitnexus/Caddyfile b/.fly/gitnexus/Caddyfile new file mode 100644 index 0000000000..ac69e6e775 --- /dev/null +++ b/.fly/gitnexus/Caddyfile @@ -0,0 +1,21 @@ +:8080 { + # Health check — unauthenticated so Fly.io can probe it + @health path /health + handle @health { + reverse_proxy localhost:4747 { + rewrite /api/info + } + } + + # All other routes require bearer token + @authed { + header Authorization "Bearer {env.API_TOKEN}" + } + + handle @authed { + reverse_proxy localhost:4747 + } + + # Reject unauthenticated requests + respond "Unauthorized" 401 +} diff --git a/.fly/gitnexus/Dockerfile b/.fly/gitnexus/Dockerfile new file mode 100644 index 0000000000..cf5cd29756 --- /dev/null +++ b/.fly/gitnexus/Dockerfile @@ -0,0 +1,29 @@ +FROM node:24-slim + +ARG GITNEXUS_VERSION=1.5.3 + +# Build tools for native addons (LadybugDB, tree-sitter), cleaned up after install +RUN apt-get update \ + && apt-get install -y --no-install-recommends python3 make g++ caddy \ + && npm install -g gitnexus@${GITNEXUS_VERSION} \ + && apt-get purge -y --auto-remove python3 make g++ \ + && rm -rf /var/lib/apt/lists/* /root/.npm + +WORKDIR /repo + +# Copy the pre-built GitNexus index (from CI artifact) +COPY .gitnexus/ .gitnexus/ + +# Register the index so `gitnexus serve` can discover it +RUN gitnexus index /repo --allow-non-git + +# Caddy reverse proxy: bearer token auth in front of gitnexus serve +# Token is set via FLY_API_SECRET (flyctl secrets set API_TOKEN=...) +COPY Caddyfile /etc/caddy/Caddyfile + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +EXPOSE 8080 + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/.fly/gitnexus/entrypoint.sh b/.fly/gitnexus/entrypoint.sh new file mode 100644 index 0000000000..8a127848b4 --- /dev/null +++ b/.fly/gitnexus/entrypoint.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +if [ -z "$API_TOKEN" ]; then + echo "ERROR: API_TOKEN secret is not set." + echo "Run: flyctl secrets set API_TOKEN=" + exit 1 +fi + +# Start gitnexus serve in background +gitnexus serve --host 127.0.0.1 --port 4747 & + +# Start caddy auth proxy in foreground +exec caddy run --config /etc/caddy/Caddyfile diff --git a/.fly/gitnexus/fly.toml b/.fly/gitnexus/fly.toml new file mode 100644 index 0000000000..086a64a640 --- /dev/null +++ b/.fly/gitnexus/fly.toml @@ -0,0 +1,24 @@ +app = 'librechat-gitnexus' +primary_region = 'iad' + +[build] + dockerfile = 'Dockerfile' + +[http_service] + internal_port = 8080 + force_https = true + auto_stop_machines = 'stop' + auto_start_machines = true + min_machines_running = 1 + +[[vm]] + size = 'shared-cpu-1x' + memory = '512mb' + +[checks] + [checks.health] + type = 'http' + port = 8080 + path = '/health' + interval = '30s' + timeout = '5s' diff --git a/.github/workflows/gitnexus-deploy.yml b/.github/workflows/gitnexus-deploy.yml new file mode 100644 index 0000000000..7ee6e23fc7 --- /dev/null +++ b/.github/workflows/gitnexus-deploy.yml @@ -0,0 +1,104 @@ +# Deploys the GitNexus index to Fly.io as a persistent MCP + REST server. +# +# Endpoints available after deploy: +# /api/mcp — MCP-over-HTTP (StreamableHTTP transport) +# /api/query — Search execution flows +# /api/search — Hybrid BM25 + semantic search +# /api/repos — List indexed repositories +# /api/info — Server version and status +# +# First-time setup: +# 1. flyctl apps create librechat-gitnexus +# 2. flyctl tokens create deploy -x 999999h +# 3. flyctl secrets set API_TOKEN=$(openssl rand -hex 32) +# 4. Add FLY_API_TOKEN as a GitHub repo secret +# +# All requests (except /health) require: Authorization: Bearer + +name: GitNexus Deploy + +on: + workflow_run: + workflows: ['GitNexus Index'] + branches: [main] + types: [completed] + workflow_dispatch: + +permissions: + actions: read + +concurrency: + group: gitnexus-deploy + cancel-in-progress: true + +env: + GITNEXUS_VERSION: '1.5.3' + +jobs: + deploy: + if: | + github.event_name == 'workflow_dispatch' || + github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout deploy config + uses: actions/checkout@v4 + with: + sparse-checkout: .fly/gitnexus + fetch-depth: 1 + + - name: Resolve index run + id: resolve + uses: actions/github-script@v7 + with: + script: | + const runId = context.payload.workflow_run?.id; + if (runId) { + core.setOutput('run_id', String(runId)); + return; + } + // workflow_dispatch: find latest successful index run on main + const { data } = await github.rest.actions.listWorkflowRuns({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'gitnexus-index.yml', + branch: 'main', + status: 'success', + per_page: 1, + }); + if (!data.workflow_runs.length) { + core.setFailed('No successful GitNexus Index runs found on main'); + return; + } + core.setOutput('run_id', String(data.workflow_runs[0].id)); + + - name: Download GitNexus index + uses: actions/download-artifact@v4 + with: + name: gitnexus-index-main + path: deploy/.gitnexus + run-id: ${{ steps.resolve.outputs.run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Prepare deploy context + run: | + cp .fly/gitnexus/Dockerfile deploy/Dockerfile + cp .fly/gitnexus/Caddyfile deploy/Caddyfile + cp .fly/gitnexus/entrypoint.sh deploy/entrypoint.sh + echo "Deploy context:" + ls -la deploy/ + ls -la deploy/.gitnexus/ + + - name: Setup Fly + uses: superfly/flyctl-actions/setup-flyctl@master + + - name: Deploy to Fly.io + working-directory: deploy + run: | + flyctl deploy \ + --config ../.fly/gitnexus/fly.toml \ + --build-arg GITNEXUS_VERSION=${{ env.GITNEXUS_VERSION }} \ + --remote-only + env: + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/.github/workflows/gitnexus-index.yml b/.github/workflows/gitnexus-index.yml new file mode 100644 index 0000000000..4fb425b4c2 --- /dev/null +++ b/.github/workflows/gitnexus-index.yml @@ -0,0 +1,91 @@ +name: GitNexus Index + +on: + push: + branches: [main, dev] + paths-ignore: ['**.md', 'docs/**', 'LICENSE', '.github/**'] + pull_request: + branches: [main, dev] + paths-ignore: ['**.md', 'docs/**', 'LICENSE', '.github/**'] + workflow_dispatch: + inputs: + embeddings: + description: 'Enable embedding generation (slow, increases index size)' + type: boolean + default: false + force: + description: 'Force full re-index' + type: boolean + default: false + +permissions: + contents: read + +concurrency: + group: gitnexus-${{ github.ref }} + cancel-in-progress: true + +env: + GITNEXUS_VERSION: '1.5.3' + +jobs: + index: + # Allow push + dispatch unconditionally; filter PRs to contributors only + if: | + github.event_name != 'pull_request' || + github.event.pull_request.author_association == 'OWNER' || + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.author_association == 'COLLABORATOR' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Cache npm store + uses: actions/cache@v4 + with: + path: ~/.npm + key: gitnexus-npm-${{ runner.os }}-${{ env.GITNEXUS_VERSION }} + restore-keys: gitnexus-npm-${{ runner.os }}- + + - name: Run GitNexus Analyze + run: | + FLAGS="--skip-agents-md --verbose" + if [ "${{ inputs.embeddings }}" = "true" ]; then + FLAGS="$FLAGS --embeddings" + fi + if [ "${{ inputs.force }}" = "true" ]; then + FLAGS="$FLAGS --force" + fi + npx --yes gitnexus@${{ env.GITNEXUS_VERSION }} analyze . $FLAGS + + - name: Verify index + run: | + if [ ! -d ".gitnexus" ] || [ ! -f ".gitnexus/meta.json" ]; then + echo "::error::GitNexus index was not created" + exit 1 + fi + echo "::group::Index metadata" + cat .gitnexus/meta.json + echo "" + echo "::endgroup::" + + - name: Upload GitNexus index + uses: actions/upload-artifact@v4 + with: + name: >- + gitnexus-index-${{ + github.event_name == 'pull_request' + && format('pr-{0}', github.event.pull_request.number) + || github.ref_name + }} + path: .gitnexus/ + retention-days: 30 From 55fc37ff494f0b22c9a60eb15e531e83ba002303 Mon Sep 17 00:00:00 2001 From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:04:15 -0700 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=94=90=20fix:=20Add=20Tenant=20Contex?= =?UTF-8?q?t=20to=20Admin=20OAuth=20Callback=20Routes=20(#12579)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔐 fix: Add Tenant Context to Admin OAuth Callback Routes * fix: add tenant context to admin local login route --- api/server/routes/admin/auth.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/server/routes/admin/auth.js b/api/server/routes/admin/auth.js index 07306ac4db..9b0ecb66a5 100644 --- a/api/server/routes/admin/auth.js +++ b/api/server/routes/admin/auth.js @@ -8,6 +8,7 @@ const { exchangeAdminCode, createSetBalanceConfig, storeAndStripChallenge, + tenantContextMiddleware, } = require('@librechat/api'); const { loginController } = require('~/server/controllers/auth/LoginController'); const { requireCapability } = require('~/server/middleware/roles/capabilities'); @@ -56,6 +57,7 @@ router.post( middleware.loginLimiter, middleware.checkBan, middleware.requireLocalAuth, + tenantContextMiddleware, requireAdminAccess, setBalanceConfig, loginController, @@ -152,6 +154,7 @@ router.get( failureMessage: true, session: false, }), + tenantContextMiddleware, retrievePkceChallenge('openid'), requireAdminAccess, setBalanceConfig, @@ -190,6 +193,7 @@ router.post( failureMessage: true, session: false, }), + tenantContextMiddleware, retrievePkceChallenge('saml'), requireAdminAccess, setBalanceConfig, @@ -229,6 +233,7 @@ router.get( failureMessage: true, session: false, }), + tenantContextMiddleware, retrievePkceChallenge('google'), requireAdminAccess, setBalanceConfig, @@ -268,6 +273,7 @@ router.get( failureMessage: true, session: false, }), + tenantContextMiddleware, retrievePkceChallenge('github'), requireAdminAccess, setBalanceConfig, @@ -307,6 +313,7 @@ router.get( failureMessage: true, session: false, }), + tenantContextMiddleware, retrievePkceChallenge('discord'), requireAdminAccess, setBalanceConfig, @@ -346,6 +353,7 @@ router.get( failureMessage: true, session: false, }), + tenantContextMiddleware, retrievePkceChallenge('facebook'), requireAdminAccess, setBalanceConfig, @@ -384,6 +392,7 @@ router.post( failureMessage: true, session: false, }), + tenantContextMiddleware, retrievePkceChallenge('apple'), requireAdminAccess, setBalanceConfig, From b8158a613ea59f176e07e3f971945deeda37bbdf Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:02:31 +0200 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=8E=A8=20refacto?= =?UTF-8?q?r:=20Prompts/Sidebar=20styles=20for=20improved=20UI=20Consisten?= =?UTF-8?q?cy=20(#12426)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style(sidebar): polish button styles, icon sizes, and nav separator - Match new chat button style to sidebar toggle - Unify icon sizes and stroke weights across sidebar icons - Add separator between new chat and nav links feat(prompts): inline prompt editing with route-based navigation - Show prompts dashboard inline when editing/creating from chat - Use route-based navigation instead of state-driven view switching - Strip inline view to form-only without duplicate sidebar style(prompts): unify borders, backgrounds, and admin controls - Unify borders to border-medium and remove opaque backgrounds - Add admin/advanced controls to sidebar - Match chat background, move advanced toggle to versions panel refactor(prompts): consolidate list actions into dropdown with compact layout - Unify prompt list to single ChatGroupItem with all actions - Consolidate actions into dropdown menu - Distinct dropdown icons with status icon tooltips - Remove rename action, use Trash icon matching convo items refactor(prompts): inline-edit title with click-to-edit and save indicator - Click title text to enter edit mode with pencil icon on hover - Clean title input with inline save status indicator - Uniform h-9 header height, smaller title, icon-only save status - Remove separate edit icon button and confirm/cancel controls style(prompts): sticky click-to-edit pill with surface-primary background fix: preserve AuthContext identity across Vite HMR updates createContext() was re-executed on every HMR module replacement, creating a new context object that disconnected the existing provider from its consumers. useAuthContext() would throw because useContext(newContext) returned undefined while the provider was still on the old context. Stash the context object in import.meta.hot.data so it survives HMR re-execution. Dead-code-eliminated in production builds. feat: add advanced prompts editor toggle to Chat settings - Add AdvancedPrompts switch in Settings > Chat that maps the promptsEditorMode enum (simple/advanced) to a boolean toggle - Preserve existing behavior: switching to simple forces alwaysMakeProd - Add focus styling to PromptName input (border-medium on focus, outline on focus-visible, no ring) - Add localization keys: com_nav_advanced_prompts, com_nav_advanced_prompts_desc - Various Prompts UI refinements style(sidebar): update nav icons and reorder links for clarity - Prompts: MessageSquareQuote → NotebookPen (distinct from chat bubble) - Agent Builder: Blocks → Bot (no longer identical to Assistants) - Memories: Database → BrainCircuit (brain + AI circuit) - Parameters: Settings2 → SlidersHorizontal (tuning-specific) - Reorder: Chat → Prompts → Agents → Assistants → Memories → Bookmarks → Files → Parameters → MCP (frequency and grouping) refactor(prompts): remove AdvancedSwitch component The simple/advanced toggle is now in Settings > Chat, making the standalone AdvancedSwitch redundant. Remove the component, its barrel exports, and all render sites (PromptForm, DashBreadcrumb, PromptsView). style(prompts): tighten panel layout and spacing to match sidebar conventions - Remove top margin from form wrapper, add mt-2 to header row - Reduce header gaps and remove flex-wrap for tighter alignment - Shrink prompt title from text-lg/h-9 to text-base/h-8 - Add pl-2 inner padding to PromptName input and static button - Compact versions panel and deploy button container padding - Reduce editor header and content spacing - Compact mobile versions panel header fix(prompts): rebuild mobile versions panel with proper transitions - Add background (bg-surface-primary-alt) and border-l so content doesn't bleed through - Replace conditional render with CSS opacity/translate so both overlay fade-out and panel slide-out animate on close - Use inert attribute to disable focus when panel is hidden - Replace raw × character with X icon from lucide-react - Use size-icon button variant for close instead of text sm - Use Tailwind classes (w-80, translate-x-full) instead of inline style object for width and transform - Remove unused sidePanelWidth constant - Overlay stays in DOM with pointer-events-none when hidden chore: remove stale tooling artifacts Remove .bg-shell/manifest.json and .gsd symlink that were accidentally committed — these are local dev tooling files. fix: resolve review issues in PromptName and ChatGroupItem - Fix Escape/onBlur race in PromptName via cancelledRef guard - Clear existing timer before creating new one to prevent status flicker - Guard delete mutation against empty group id - Attach menuButtonRef to MenuButton for proper focus restoration after preview dialog closes fix: address review findings across prompts UI and sidebar PromptName: - Fix Enter key double-save: set skipBlurRef before inline save to prevent onBlur from re-firing saveName on input unmount - Add isError prop to distinguish mutation failure from success, preventing false 'saved' checkmark on error - Remove dead cn ternary that always evaluated to opacity-100 - Remove unused cn import PromptForm: - Pass isError from updateGroupMutation to PromptName - Remove dead default arg (= {}) on function component ChatGroupItem: - Stabilize handleDelete via ref pattern to prevent memo-defeating recreation on every render (deleteGroup is a new ref each render) - Disable delete button during mutation to prevent double-fire ExpandedPanel: - Restore element for NewChatButton to fix middle-click (open in new tab) regression caused by anchor-to-button migration DashGroupItem: - Delete dead file (no longer exported or imported anywhere) fix: address review findings across prompts UI and sidebar - PromptName: render error state (red X) when save fails, extract shared commitName helper to deduplicate blur/Enter save paths - ChatGroupItem: navigate away after deleting the active prompt in sidebar view; use context-aware route prefix (/prompts vs /d/prompts) for edit and card-click navigation - InlinePromptsView: redirect to /c/new when user lacks prompts access instead of rendering a blank screen - Remove dead ManagePrompts component and its barrel exports (no remaining consumers after GroupSidePanel cleanup) fix: remove duplicate showThinking Recoil atom key The 'showThinking' key was defined in both store/settings.ts (Recoil) and store/showThinking.ts (Jotai). Only the Jotai atom is consumed; the stale Recoil duplicate causes 'A key option with a unique string value must be provided' at startup. refactor: remove /d/prompts dashboard route and dead code The prompts UI now lives at /prompts/* inline under the chat layout. The old /d/prompts/* dashboard route, its layout (PromptsView), and its breadcrumb (DashBreadcrumb) are no longer used. - Delete PromptsView and DashBreadcrumb (zero consumers) - Delete BackToChat button (zero consumers) - Replace /d/prompts/* child routes with a redirect to /prompts/new - Add /prompts index route that redirects to /prompts/new - Update all /d/prompts navigation to /prompts: - ChatGroupItem: always use /prompts prefix - NoPromptGroup: navigate to /prompts - CreatePromptForm: fallback navigate to /prompts/:id - CreatePromptButton: simplified to /prompts/new (no dual-path) - Strip GroupSidePanel of dashboard-only breadcrumb nav, recoil state clearing, and useDashboardContext dependency refactor: remove dead Dashboard code and unused translation keys - Delete DashboardContext provider (zero remaining consumers) - Simplify DashboardRoute layout to a plain Outlet - Remove commented-out file/vector-store route blocks - Change catch-all redirect from /d/files to /c/new - Remove 7 unused translation keys (com_nav_toggle_sidebar, com_ui_back_to_chat, com_ui_dashboard, com_ui_delete_prompt_name, com_ui_global_group, com_ui_prompt_renamed, com_ui_rename_prompt, com_ui_rename_prompt_name) fix: add Babel plugin to transform import.meta.hot for Jest `babel-plugin-transform-import-meta` handles standard properties (url, filename, dirname, resolve) but not Vite's `hot` property. Jest runs in CommonJS where `import.meta` is unavailable, so `import.meta.hot` in AuthContext.tsx (added for HMR preservation) causes a SyntaxError that breaks 27 test suites. Add a small Babel plugin that replaces `import.meta.hot` with `undefined` during Jest transforms, making the HMR guard blocks dead-code in tests. fix: address review findings — inert typing, accessibility, and cleanup - Add React type augmentation for `inert` attribute (React 18 compat) - Replace spread hack `{...{ inert: }}` with direct prop in all 3 files - Add `aria-hidden` to mobile overlay in PromptForm for screen readers - Simplify deleteGroupRef pattern to direct mutation call - Remove unused `useEffect` import and stale useMemo dependency - Add clarifying comment for skipBlurRef mechanism in PromptName fix: mobile UX for marketplace and prompts views - Remove mobile new chat button from chat history section - Add OpenSidebar entry points to marketplace and prompts views on mobile - Move marketplace admin settings to a compact mobile top row - Restructure prompt forms to surface category selector beside sidebar toggle - Make versions panel slide content like the main sidebar and drop redundant borders - Collapse versions button to icon-only on mobile - Remove theme selector from prompt panel navigation * fix: address PR review findings for prompts refactor - Preserve prompt ID in /d/prompts/:id → /prompts/:id redirect - Gate PreviewPrompt and VariableDialog behind isChatRoute to avoid mounting dead dialogs in dashboard mode - Add onError handler to useDeletePromptGroup and close dialog on success - Use useId() instead of hardcoded labelId in AdvancedPrompts - Extract shared lazy loader for InlinePromptsView routes * fix: complete review fixes for prompts refactor - Move OGDialog (delete) inside isChatRoute gate with other dialogs - Use useId() for both Switch id and label id in AdvancedPrompts - Add com_ui_prompt_delete_error i18n key for actionable error context - Drop no-op useCallback on handleDelete (unstable deleteGroup dep) * refactor: hoist promptPath to module-scope constant Eliminates stale-closure lint concern in dropdownItems useMemo and removes the unnecessary dep array entry from onCardClick. * refactor(sidebar): update icons and reorder links for clarity - Replace Blocks icon with OpenAIMinimalIcon for the Assistant Builder link. - Update Memories icon from BrainCircuit to Brain. - Reintroduce Prompts link conditionally based on access permissions. - Change Conversations icon from MessageSquare to MessagesSquare for consistency. * refactor(sidebar): update icons and improve file attachment link - Replace NewChatIcon with SquarePen in the NewChatButton for better visual consistency. - Change AttachmentIcon to Paperclip in the file attachment link for clarity. * refactor(sidebar): update file attachment icon for consistency - Replace Paperclip icon with AttachmentIcon in the file attachment link for improved clarity and visual consistency. * refactor(admin-settings): remove unused button and streamline dialog integration - Eliminate the Admin button and its associated icon from the AdminSettings component for a cleaner interface. - Simplify the confirm dialog integration by directly using the OGDialog without the button trigger. * fix: context HMR issue * style(prompts): enhance component structure and accessibility - Update AutoSendPrompt button class for improved styling. - Refactor List component to streamline loading and empty states. - Ensure FilterPrompts handles context gracefully with null checks. - Modify GroupSidePanel to prevent rendering without context. - Simplify PromptsAccordion layout for better readability. - Adjust CategoryIcon fallback behavior for undefined categories. * refactor(useMCPServerManager): clean up import statements - Remove duplicate import of MCPServerInitState for better clarity and organization. - Adjust import order to maintain consistency with project structure. * refactor(GroupSidePanel): restructure layout for improved readability and accessibility - Adjust the structure of the GroupSidePanel component to enhance layout clarity. - Move the PanelNavigation component into a more appropriate position within the hierarchy. - Ensure consistent styling and behavior based on the isChatRoute condition. * style(GroupSidePanel): adjust padding for improved layout consistency - Update padding in the GroupSidePanel component to enhance visual alignment and readability. - Ensure consistent styling across the component for a better user experience. * fix(Conversations): add cache clearing and row height recomputation on search query change - Implement useEffect to clear cache and recompute row heights when the search query changes. - Enhance performance and responsiveness of the Conversations component during search operations. * refactor(GroupSidePanel, PromptsAccordion): simplify layout and improve styling * chore: import order * fix: redirect users without CREATE permission from /prompts/new Users with USE but not CREATE permission were seeing a blank page at /prompts/new because InlinePromptsView only checked USE access. CreatePromptForm's internal redirect was bypassed by the onSuccess prop always being passed. Add CREATE check in InlinePromptsView so the redirect happens before CreatePromptForm mounts. * fix: restore dropdown actions for all routes and handle non-creator landing - Remove isChatRoute gate on dropdown menu so preview, edit, and delete actions are available on the prompts management route - Un-gate PreviewPrompt and OGDialog (delete) since both are triggered from the now-always-visible dropdown - Keep VariableDialog gated behind isChatRoute (chat submission only) - Show EmptyPromptPreview for non-creators at /prompts/new instead of redirecting to /c/new, so they stay in the prompts section with sidebar access to browse existing prompts * fix: add isPublic to TPromptGroup type The database schema (IPromptGroup in data-schemas) has isPublic but the shared TPromptGroup type in data-provider was missing it, causing a TS2339 error in ChatGroupItem. * fix: prevent duplicate rename and restore name on error in PromptName - Block re-entry to edit mode while a save is in flight by guarding the click handler with isLoading/saveStatus checks - Reset newName to the prop value when mutation fails so the UI doesn't display the unsaved name after the error icon clears * fix: address review findings across prompts refactor - Consolidate duplicate usePromptGroupsContext() calls in PromptForm - Remove invalid aria-labelledby (text string, not ID) from AutoSendPrompt checkbox that is already aria-hidden - Remove useMemo wrapping trivial `disabled ?? false` in ToolsDropdown - Remove dead context spread in PromptsAccordion (GroupSidePanel reads context internally) - Wrap search cache-clear effect in requestAnimationFrame to match favorites effect pattern in Conversations - Use Set for O(1) lookups in MCPSelect server filtering - Fix unnecessary JSX expression wrapper on string literal in CreatePromptButton Link * style(PromptTextCard): update icon classes for improved accessibility - Add 'text-text-secondary' class to Check and Copy icons for better visibility and consistency in the PromptTextCard component. --------- Co-authored-by: Danny Avila --- client/babel.config.cjs | 1 + client/src/Providers/BadgeRowContext.tsx | 6 +- client/src/Providers/DashboardContext.tsx | 7 - client/src/Providers/DragDropContext.tsx | 14 +- client/src/Providers/PromptGroupsContext.tsx | 6 +- client/src/Providers/index.ts | 1 - client/src/components/Agents/Marketplace.tsx | 14 +- .../Agents/MarketplaceAdminSettings.tsx | 13 +- .../src/components/Chat/Input/Artifacts.tsx | 7 +- .../components/Chat/Input/CodeInterpreter.tsx | 6 +- .../src/components/Chat/Input/FileSearch.tsx | 4 +- .../src/components/Chat/Input/MCPSelect.tsx | 56 ++-- .../src/components/Chat/Input/MCPSubMenu.tsx | 32 +- .../components/Chat/Input/PromptsCommand.tsx | 5 +- .../src/components/Chat/Input/ToolDialogs.tsx | 22 +- .../components/Chat/Input/ToolsDropdown.tsx | 109 +++---- .../src/components/Chat/Input/WebSearch.tsx | 12 +- .../Conversations/Conversations.tsx | 10 + .../Nav/SettingsTabs/Chat/AdvancedPrompts.tsx | 46 +++ .../components/Nav/SettingsTabs/Chat/Chat.tsx | 4 + .../Prompts/buttons/AdminSettings.tsx | 16 +- .../Prompts/buttons/AdvancedSwitch.tsx | 52 --- .../Prompts/buttons/AutoSendPrompt.tsx | 4 +- .../components/Prompts/buttons/BackToChat.tsx | 33 -- .../Prompts/buttons/CreatePromptButton.tsx | 2 +- .../Prompts/buttons/ManagePrompts.tsx | 32 -- .../src/components/Prompts/buttons/index.ts | 3 - .../Prompts/dialogs/DeletePrompt.tsx | 3 +- .../Prompts/dialogs/SharePrompt.tsx | 3 +- .../Prompts/display/PromptDetails.tsx | 2 +- .../Prompts/display/PromptTextCard.tsx | 8 +- .../Prompts/display/PromptVariables.tsx | 12 +- .../Prompts/display/PromptVersions.tsx | 6 +- .../Prompts/editor/PromptEditor.tsx | 14 +- .../Prompts/fields/CategorySelector.tsx | 4 +- .../src/components/Prompts/fields/Command.tsx | 4 +- .../components/Prompts/fields/Description.tsx | 4 +- .../components/Prompts/fields/PromptName.tsx | 196 ++++++----- .../Prompts/forms/CreatePromptForm.tsx | 32 +- .../components/Prompts/forms/PromptForm.tsx | 160 ++++----- .../Prompts/forms/PromptLabelsForm.tsx | 4 +- client/src/components/Prompts/index.ts | 20 +- .../Prompts/layouts/InlinePromptsView.tsx | 48 +++ .../Prompts/layouts/PromptsView.tsx | 140 -------- .../src/components/Prompts/layouts/index.ts | 2 +- .../Prompts/lists/ChatGroupItem.tsx | 303 ++++++++++++------ .../Prompts/lists/DashGroupItem.tsx | 223 ------------- client/src/components/Prompts/lists/List.tsx | 71 ++-- .../Prompts/lists/NoPromptGroup.tsx | 2 +- client/src/components/Prompts/lists/index.ts | 1 - .../Prompts/sidebar/FilterPrompts.tsx | 5 +- .../Prompts/sidebar/GroupSidePanel.tsx | 98 ++---- .../Prompts/sidebar/PanelNavigation.tsx | 8 +- .../Prompts/sidebar/PromptsAccordion.tsx | 22 +- .../components/Prompts/utils/CategoryIcon.tsx | 15 +- .../UnifiedSidebar/ConversationsSection.tsx | 36 +-- .../UnifiedSidebar/ExpandedPanel.tsx | 21 +- .../UnifiedSidebar/UnifiedSidebar.tsx | 2 +- client/src/hooks/AuthContext.tsx | 6 +- client/src/hooks/MCP/useMCPServerManager.ts | 2 +- client/src/hooks/Nav/useSideNavLinks.ts | 78 +++-- .../src/hooks/Nav/useUnifiedSidebarLinks.ts | 4 +- client/src/locales/en/translation.json | 11 +- client/src/routes/Dashboard.tsx | 75 +---- client/src/routes/Layouts/DashBreadcrumb.tsx | 104 ------ client/src/routes/Layouts/Dashboard.tsx | 28 +- client/src/routes/Root.tsx | 2 +- client/src/routes/index.tsx | 17 + client/src/store/settings.ts | 1 - ...babel-plugin-transform-import-meta-hot.cjs | 31 ++ packages/client/src/svgs/MCPIcon.tsx | 6 +- packages/data-provider/src/types.ts | 1 + 72 files changed, 936 insertions(+), 1416 deletions(-) delete mode 100644 client/src/Providers/DashboardContext.tsx create mode 100644 client/src/components/Nav/SettingsTabs/Chat/AdvancedPrompts.tsx delete mode 100644 client/src/components/Prompts/buttons/AdvancedSwitch.tsx delete mode 100644 client/src/components/Prompts/buttons/BackToChat.tsx delete mode 100644 client/src/components/Prompts/buttons/ManagePrompts.tsx create mode 100644 client/src/components/Prompts/layouts/InlinePromptsView.tsx delete mode 100644 client/src/components/Prompts/layouts/PromptsView.tsx delete mode 100644 client/src/components/Prompts/lists/DashGroupItem.tsx delete mode 100644 client/src/routes/Layouts/DashBreadcrumb.tsx create mode 100644 client/test/babel-plugin-transform-import-meta-hot.cjs diff --git a/client/babel.config.cjs b/client/babel.config.cjs index 44b0501a61..270f18f1a0 100644 --- a/client/babel.config.cjs +++ b/client/babel.config.cjs @@ -14,6 +14,7 @@ module.exports = { */ plugins: [ '@babel/plugin-transform-runtime', + './test/babel-plugin-transform-import-meta-hot.cjs', 'babel-plugin-transform-import-meta', 'babel-plugin-transform-vite-meta-env', 'babel-plugin-replace-ts-export-assignment', diff --git a/client/src/Providers/BadgeRowContext.tsx b/client/src/Providers/BadgeRowContext.tsx index dce1c38a78..1bedcec66f 100644 --- a/client/src/Providers/BadgeRowContext.tsx +++ b/client/src/Providers/BadgeRowContext.tsx @@ -29,11 +29,7 @@ interface BadgeRowContextType { const BadgeRowContext = createContext(undefined); export function useBadgeRowContext() { - const context = useContext(BadgeRowContext); - if (context === undefined) { - throw new Error('useBadgeRowContext must be used within a BadgeRowProvider'); - } - return context; + return useContext(BadgeRowContext); } interface BadgeRowProviderProps { diff --git a/client/src/Providers/DashboardContext.tsx b/client/src/Providers/DashboardContext.tsx deleted file mode 100644 index f33a240d00..0000000000 --- a/client/src/Providers/DashboardContext.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createContext, useContext } from 'react'; -type TDashboardContext = { - prevLocationPath: string; -}; - -export const DashboardContext = createContext({} as TDashboardContext); -export const useDashboardContext = () => useContext(DashboardContext); diff --git a/client/src/Providers/DragDropContext.tsx b/client/src/Providers/DragDropContext.tsx index b519c0171f..ed4dddb58f 100644 --- a/client/src/Providers/DragDropContext.tsx +++ b/client/src/Providers/DragDropContext.tsx @@ -83,10 +83,14 @@ export function DragDropProvider({ children }: { children: React.ReactNode }) { return {children}; } +const defaultDragDropValue: DragDropContextValue = { + conversationId: undefined, + agentId: undefined, + endpoint: undefined, + endpointType: undefined, + useResponsesApi: undefined, +}; + export function useDragDropContext() { - const context = useContext(DragDropContext); - if (!context) { - throw new Error('useDragDropContext must be used within DragDropProvider'); - } - return context; + return useContext(DragDropContext) ?? defaultDragDropValue; } diff --git a/client/src/Providers/PromptGroupsContext.tsx b/client/src/Providers/PromptGroupsContext.tsx index 3df373b165..147d2bc336 100644 --- a/client/src/Providers/PromptGroupsContext.tsx +++ b/client/src/Providers/PromptGroupsContext.tsx @@ -77,9 +77,5 @@ export const PromptGroupsProvider = ({ children }: { children: ReactNode }) => { }; export const usePromptGroupsContext = () => { - const context = useContext(PromptGroupsContext); - if (!context) { - throw new Error('usePromptGroupsContext must be used within a PromptGroupsProvider'); - } - return context; + return useContext(PromptGroupsContext); }; diff --git a/client/src/Providers/index.ts b/client/src/Providers/index.ts index 3ae90e189c..aac5fccc21 100644 --- a/client/src/Providers/index.ts +++ b/client/src/Providers/index.ts @@ -10,7 +10,6 @@ export * from './EditorContext'; export * from './ChatFormContext'; export * from './BookmarkContext'; export * from './MessageContext'; -export * from './DashboardContext'; export * from './AssistantsContext'; export * from './AgentsContext'; export * from './AssistantsMapContext'; diff --git a/client/src/components/Agents/Marketplace.tsx b/client/src/components/Agents/Marketplace.tsx index 816705a0db..adf406f7b0 100644 --- a/client/src/components/Agents/Marketplace.tsx +++ b/client/src/components/Agents/Marketplace.tsx @@ -1,11 +1,12 @@ import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react'; -import { useSearchParams, useParams, useNavigate } from 'react-router-dom'; import { useMediaQuery } from '@librechat/client'; import { PermissionTypes, Permissions } from 'librechat-data-provider'; +import { useSearchParams, useParams, useNavigate } from 'react-router-dom'; import type t from 'librechat-data-provider'; import { useDocumentTitle, useHasAccess, useLocalize, TranslationKeys } from '~/hooks'; import { useGetEndpointsQuery, useGetAgentCategoriesQuery } from '~/data-provider'; import MarketplaceAdminSettings from './MarketplaceAdminSettings'; +import OpenSidebar from '~/components/Chat/Menus/OpenSidebar'; import { SidePanelGroup } from '~/components/SidePanel'; import CategoryTabs from './CategoryTabs'; import SearchBar from './SearchBar'; @@ -215,14 +216,19 @@ const AgentMarketplace: React.FC = ({ className = '' }) = )} {/* Sticky wrapper for search bar and categories */} -
+
+
+ + +
{/* Search bar */}
{/* TODO: Remove this once we have a better way to handle admin settings */} - {/* Admin Settings */} - +
+ +
{/* Category tabs */} diff --git a/client/src/components/Agents/MarketplaceAdminSettings.tsx b/client/src/components/Agents/MarketplaceAdminSettings.tsx index c3d7dedda3..be62064aaf 100644 --- a/client/src/components/Agents/MarketplaceAdminSettings.tsx +++ b/client/src/components/Agents/MarketplaceAdminSettings.tsx @@ -10,7 +10,7 @@ const permissions: PermissionConfig[] = [ { permission: Permissions.USE, labelKey: 'com_ui_marketplace_allow_use' }, ]; -const MarketplaceAdminSettings = () => { +const MarketplaceAdminSettings = ({ compact = false }: { compact?: boolean }) => { const localize = useLocalize(); const { showToast } = useToastContext(); @@ -23,7 +23,16 @@ const MarketplaceAdminSettings = () => { }, }); - const trigger = ( + const trigger = compact ? ( + + ) : (
))} +
+ +
); diff --git a/client/src/components/Prompts/buttons/AdminSettings.tsx b/client/src/components/Prompts/buttons/AdminSettings.tsx index 25e6df05a8..7168217694 100644 --- a/client/src/components/Prompts/buttons/AdminSettings.tsx +++ b/client/src/components/Prompts/buttons/AdminSettings.tsx @@ -1,7 +1,6 @@ import { useState } from 'react'; -import { ShieldEllipsis } from 'lucide-react'; import { Permissions, PermissionTypes } from 'librechat-data-provider'; -import { OGDialog, OGDialogTemplate, Button, useToastContext } from '@librechat/client'; +import { OGDialog, OGDialogTemplate, useToastContext } from '@librechat/client'; import type { PermissionConfig } from '~/components/ui'; import { useUpdatePromptPermissionsMutation } from '~/data-provider'; import { AdminSettingsDialog } from '~/components/ui'; @@ -39,18 +38,6 @@ const AdminSettings = () => { setConfirmAdminUseChange({ newValue, callback: onChange }); }; - const trigger = ( - - ); - const confirmDialog = ( { permissions={permissions} menuId="prompt-role-dropdown" mutation={mutation} - trigger={trigger} onPermissionConfirm={handlePermissionConfirm} confirmPermissions={[Permissions.USE]} extraContent={confirmDialog} diff --git a/client/src/components/Prompts/buttons/AdvancedSwitch.tsx b/client/src/components/Prompts/buttons/AdvancedSwitch.tsx deleted file mode 100644 index 9941c78e66..0000000000 --- a/client/src/components/Prompts/buttons/AdvancedSwitch.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { useCallback, useMemo } from 'react'; -import { Sparkles, Layers } from 'lucide-react'; -import { useRecoilState, useSetRecoilState } from 'recoil'; -import { PromptsEditorMode } from '~/common'; -import { Radio } from '@librechat/client'; -import { useLocalize } from '~/hooks'; -import store from '~/store'; - -const { promptsEditorMode, alwaysMakeProd } = store; - -const AdvancedSwitch = () => { - const localize = useLocalize(); - const [mode, setMode] = useRecoilState(promptsEditorMode); - const setAlwaysMakeProd = useSetRecoilState(alwaysMakeProd); - - const options = useMemo( - () => [ - { - value: PromptsEditorMode.SIMPLE, - label: localize('com_ui_simple'), - icon: , - }, - { - value: PromptsEditorMode.ADVANCED, - label: localize('com_ui_advanced'), - icon: , - }, - ], - [localize], - ); - - const handleChange = useCallback( - (value: string) => { - if (value === PromptsEditorMode.SIMPLE) { - setAlwaysMakeProd(true); - } - setMode(value as PromptsEditorMode); - }, - [setMode, setAlwaysMakeProd], - ); - - return ( - - ); -}; - -export default AdvancedSwitch; diff --git a/client/src/components/Prompts/buttons/AutoSendPrompt.tsx b/client/src/components/Prompts/buttons/AutoSendPrompt.tsx index c2ebee70c5..759c33739e 100644 --- a/client/src/components/Prompts/buttons/AutoSendPrompt.tsx +++ b/client/src/components/Prompts/buttons/AutoSendPrompt.tsx @@ -25,13 +25,13 @@ export default function AutoSendPrompt({ onClick={() => handleCheckedChange(!autoSendPrompts)} aria-label={localize('com_nav_auto_send_prompts')} aria-pressed={autoSendPrompts} - className={autoSendPrompts ? 'bg-surface-hover hover:bg-surface-hover' : ''} + className={`relative h-9 w-full gap-2 rounded-lg border-border-light font-medium ${autoSendPrompts ? 'bg-surface-hover hover:bg-surface-hover' : ''}`} >
- - ); -} diff --git a/client/src/components/Prompts/buttons/CreatePromptButton.tsx b/client/src/components/Prompts/buttons/CreatePromptButton.tsx index 016721d509..13dd20b11f 100644 --- a/client/src/components/Prompts/buttons/CreatePromptButton.tsx +++ b/client/src/components/Prompts/buttons/CreatePromptButton.tsx @@ -27,7 +27,7 @@ export default function CreatePromptButton() { className="size-9 shrink-0 bg-transparent" aria-label={localize('com_ui_create_prompt')} > - +