From 873f446f8e6cf8946c77ee7853f0165b1a0dcf16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Airam=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= <100208966+Airamhh@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:13:53 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=95=B5=EF=B8=8F=20fix:=20`remoteAgents`?= =?UTF-8?q?=20Field=20Omitted=20from=20Config=20(#12150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: include remoteAgents config in loadDefaultInterface The loadDefaultInterface function was not passing the remoteAgents configuration from librechat.yaml to the permission system, causing remoteAgents permissions to never update from the YAML config even when explicitly configured. This fix adds the missing remoteAgents field to the returned loadedInterface object, allowing the permission update system to properly detect and apply remoteAgents configuration from the YAML file. Fixes remote agents (API) configuration not being applied from librechat.yaml * test: Add remoteAgents permission tests for USER and ADMIN roles Introduced new tests to validate the application of remoteAgents configuration in user permissions. The tests cover scenarios for explicit configuration, full enablement, and default role behavior when remoteAgents are not configured. This ensures that permissions are correctly applied based on the provided configuration, addressing a regression related to the omission of remoteAgents in the loadDefaultInterface function. --------- Co-authored-by: Airam Hernández Hernández Co-authored-by: Danny Avila --- packages/api/src/app/permissions.spec.ts | 94 ++++++++++++++++++++++ packages/data-schemas/src/app/interface.ts | 1 + 2 files changed, 95 insertions(+) diff --git a/packages/api/src/app/permissions.spec.ts b/packages/api/src/app/permissions.spec.ts index 41014dc200..7ab7e0d0d1 100644 --- a/packages/api/src/app/permissions.spec.ts +++ b/packages/api/src/app/permissions.spec.ts @@ -2100,4 +2100,98 @@ describe('updateInterfacePermissions - permissions', () => { expect(userCall[1][PermissionTypes.MCP_SERVERS]).toHaveProperty(Permissions.SHARE_PUBLIC); expect(userCall[1][PermissionTypes.MCP_SERVERS]).not.toHaveProperty(Permissions.SHARE); }); + + it('should apply explicit remoteAgents config to USER permissions (regression: loadDefaultInterface omission)', async () => { + const config = { + interface: { + remoteAgents: { use: true, create: true, share: false, public: false }, + }, + }; + const configDefaults = { interface: {} } as TConfigDefaults; + const interfaceConfig = await loadDefaultInterface({ config, configDefaults }); + const appConfig = { config, interfaceConfig } as unknown as AppConfig; + + await updateInterfacePermissions({ + appConfig, + getRoleByName: mockGetRoleByName, + updateAccessPermissions: mockUpdateAccessPermissions, + }); + + const userCall = mockUpdateAccessPermissions.mock.calls.find( + (call) => call[0] === SystemRoles.USER, + ); + const adminCall = mockUpdateAccessPermissions.mock.calls.find( + (call) => call[0] === SystemRoles.ADMIN, + ); + + expect(userCall[1][PermissionTypes.REMOTE_AGENTS]).toEqual({ + [Permissions.USE]: true, + [Permissions.CREATE]: true, + [Permissions.SHARE]: false, + [Permissions.SHARE_PUBLIC]: false, + }); + + expect(adminCall[1][PermissionTypes.REMOTE_AGENTS]).toEqual({ + [Permissions.USE]: true, + [Permissions.CREATE]: true, + [Permissions.SHARE]: false, + [Permissions.SHARE_PUBLIC]: false, + }); + }); + + it('should enable all remoteAgents permissions when fully enabled in config', async () => { + const config = { + interface: { + remoteAgents: { use: true, create: true, share: true, public: true }, + }, + }; + const configDefaults = { interface: {} } as TConfigDefaults; + const interfaceConfig = await loadDefaultInterface({ config, configDefaults }); + const appConfig = { config, interfaceConfig } as unknown as AppConfig; + + await updateInterfacePermissions({ + appConfig, + getRoleByName: mockGetRoleByName, + updateAccessPermissions: mockUpdateAccessPermissions, + }); + + const userCall = mockUpdateAccessPermissions.mock.calls.find( + (call) => call[0] === SystemRoles.USER, + ); + + expect(userCall[1][PermissionTypes.REMOTE_AGENTS]).toEqual({ + [Permissions.USE]: true, + [Permissions.CREATE]: true, + [Permissions.SHARE]: true, + [Permissions.SHARE_PUBLIC]: true, + }); + }); + + it('should use role defaults for remoteAgents when not configured (all false for USER)', async () => { + const config = { + interface: { + bookmarks: true, + }, + }; + const configDefaults = { interface: {} } as TConfigDefaults; + const interfaceConfig = await loadDefaultInterface({ config, configDefaults }); + const appConfig = { config, interfaceConfig } as unknown as AppConfig; + + await updateInterfacePermissions({ + appConfig, + getRoleByName: mockGetRoleByName, + updateAccessPermissions: mockUpdateAccessPermissions, + }); + + const userCall = mockUpdateAccessPermissions.mock.calls.find( + (call) => call[0] === SystemRoles.USER, + ); + + expect(userCall[1][PermissionTypes.REMOTE_AGENTS]).toEqual({ + [Permissions.USE]: false, + [Permissions.CREATE]: false, + [Permissions.SHARE]: false, + [Permissions.SHARE_PUBLIC]: false, + }); + }); }); diff --git a/packages/data-schemas/src/app/interface.ts b/packages/data-schemas/src/app/interface.ts index f8afdefd33..1701a22fad 100644 --- a/packages/data-schemas/src/app/interface.ts +++ b/packages/data-schemas/src/app/interface.ts @@ -55,6 +55,7 @@ export async function loadDefaultInterface({ fileCitations: interfaceConfig?.fileCitations, peoplePicker: interfaceConfig?.peoplePicker, marketplace: interfaceConfig?.marketplace, + remoteAgents: interfaceConfig?.remoteAgents, }); return loadedInterface;