🕵️ fix: remoteAgents Field Omitted from Config (#12150)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions

* 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 <airam.hernandez@intelequia.com>
Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Airam Hernández Hernández 2026-03-09 15:13:53 +00:00 committed by GitHub
parent 32cadb1cc5
commit 873f446f8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 95 additions and 0 deletions

View file

@ -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,
});
});
});

View file

@ -55,6 +55,7 @@ export async function loadDefaultInterface({
fileCitations: interfaceConfig?.fileCitations,
peoplePicker: interfaceConfig?.peoplePicker,
marketplace: interfaceConfig?.marketplace,
remoteAgents: interfaceConfig?.remoteAgents,
});
return loadedInterface;