Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"dependencies": {
"@clickhouse/click-ui": "0.2.0-rc.4",
"@librechat/data-schemas": "^0.0.51",
"@librechat/data-schemas": "^0.0.53",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-devtools": "0.10.0",
"@tanstack/react-query": "5.95.2",
Expand All @@ -41,7 +41,7 @@
"i18next-browser-languagedetector": "^8.2.1",
"input-otp": "^1.4.2",
"js-yaml": "^4.1.1",
"librechat-data-provider": "^0.8.502",
"librechat-data-provider": "^0.8.505",
Comment thread
dustinhealy marked this conversation as resolved.
"lucide-react": "^0.545.0",
"prom-client": "^15.1.3",
"react": "^19.2.0",
Expand Down
1 change: 1 addition & 0 deletions src/components/access/RolePermissionsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const PERMISSION_TYPE_ORDER: PermissionTypes[] = [
PermissionTypes.MCP_SERVERS,
PermissionTypes.REMOTE_AGENTS,
PermissionTypes.SKILLS,
PermissionTypes.SHARED_LINKS,
PermissionTypes.BOOKMARKS,
PermissionTypes.MULTI_CONVO,
PermissionTypes.TEMPORARY_CHAT,
Expand Down
3 changes: 1 addition & 2 deletions src/components/configuration/FieldRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,6 @@ export function FieldRenderer({
pendingResets,
schemaDefaults,
showConfiguredOnly,
alwaysShowLabels,
}: t.FieldRendererProps) {
const localize = useLocalize();
const values =
Expand Down Expand Up @@ -1267,7 +1266,7 @@ export function FieldRenderer({
pendingResets={pendingResets}
schemaDefaults={schemaDefaults}
showConfiguredOnly={showConfiguredOnly}
isSoleField={!alwaysShowLabels && fields.length === 1 && groups.length === 1}
isSoleField={false}
/>
);
})}
Expand Down
10 changes: 10 additions & 0 deletions src/components/configuration/configMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ export const SECTION_META: Record<
descriptionKey: 'com_config_section_summarization_desc',
tab: 'features',
},
messageFilter: {
titleKey: 'com_config_section_messageFilter',
descriptionKey: 'com_config_section_messageFilter_desc',
tab: 'features',
},

fileConfig: {
titleKey: 'com_config_section_file_config',
Expand Down Expand Up @@ -174,6 +179,11 @@ export const SECTION_META: Record<
descriptionKey: 'com_config_section_transactions_desc',
tab: 'system',
},
skillSync: {
titleKey: 'com_config_section_skillSync',
descriptionKey: 'com_config_section_skillSync_desc',
tab: 'system',
Comment thread
dustinhealy marked this conversation as resolved.
},
};

/** Sections omitted from the UI entirely (legacy fields pending removal from the schema). */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ function ProviderSection({
<MultiAccordion.Item id={`section-${path}`} value={path} title={title}>
{hasPrioritySplit ? (
<>
<FieldRenderer fields={priorityChildren} {...rendererProps} alwaysShowLabels />
<FieldRenderer fields={priorityChildren} {...rendererProps} />
{restChildren.length > 0 && (
<NestedGroup
label={localize('com_config_more_settings')}
Expand Down
24 changes: 14 additions & 10 deletions src/components/configuration/sections/McpServersRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const TRANSPORT_FIELDS: Record<string, string[]> = {
const ALL_TRANSPORT_KEYS = new Set(Object.values(TRANSPORT_FIELDS).flat());
const REMOTE_ONLY_FIELDS = new Set(['requiresOAuth', 'apiKey', 'oauth', 'oauth_headers']);
const REMOTE_TRANSPORTS = new Set(['sse', 'streamable-http', 'http', 'websocket']);
const HTTP_ONLY_FIELDS = new Set(['obo', 'proxy']);
const HTTP_TRANSPORTS = new Set(['sse', 'streamable-http', 'http']);

const REQUIRED_BY_TRANSPORT: Record<string, Set<string>> = {
stdio: new Set(['command', 'args']),
Expand Down Expand Up @@ -428,6 +430,10 @@ function McpEntryFields({
if (currentTransportFields.has(field.key)) {
visibleKeys.add(field.key);
}
} else if (HTTP_ONLY_FIELDS.has(field.key)) {
if (HTTP_TRANSPORTS.has(currentType)) {
visibleKeys.add(field.key);
}
} else if (REMOTE_ONLY_FIELDS.has(field.key)) {
if (isRemote) {
visibleKeys.add(field.key);
Expand Down Expand Up @@ -646,7 +652,6 @@ function CreateMcpServerDialog({
);
}


export function McpServersRenderer(props: t.FieldRendererProps) {
const {
fields,
Expand All @@ -667,7 +672,9 @@ export function McpServersRenderer(props: t.FieldRendererProps) {
const path = parentPath;
const entryPrefix = `${path}.`;
const baseValue = getValue(path, parentValue ?? EMPTY_RECORD);
const baseRecord: Record<string, t.ConfigValue> = isPlainObject(baseValue) ? baseValue : EMPTY_RECORD;
const baseRecord: Record<string, t.ConfigValue> = isPlainObject(baseValue)
? baseValue
: EMPTY_RECORD;

const editsByEntry = useMemo(() => {
const map = new Map<string, Array<{ segments: string[]; value: t.ConfigValue }>>();
Expand Down Expand Up @@ -709,9 +716,7 @@ export function McpServersRenderer(props: t.FieldRendererProps) {
seedIndex = i;
}
}
const subsequentLeaves = leafEdits
.slice(seedIndex + 1)
.filter((e) => e.segments.length > 0);
const subsequentLeaves = leafEdits.slice(seedIndex + 1).filter((e) => e.segments.length > 0);
if (subsequentLeaves.length === 0) {
return seedFromDelete ? undefined : seed;
}
Expand Down Expand Up @@ -1038,18 +1043,17 @@ const McpEntryRow = memo(function McpEntryRowImpl({
value={displayValue}
onValueChange={handleWholeEntryChange}
onRemove={isReadOnly || isLockedIdentity ? undefined : () => onRemove(entryKey)}
onRename={isReadOnly || isLockedIdentity ? undefined : (renamed) => onRename(entryKey, renamed)}
onRename={
isReadOnly || isLockedIdentity ? undefined : (renamed) => onRename(entryKey, renamed)
}
disabled={isReadOnly}
defaultExpanded={justAdded}
renderFields={renderEntryFields}
/>
);
});

function lookupLeaf(
obj: unknown,
segments: string[],
): t.ConfigValue | undefined {
function lookupLeaf(obj: unknown, segments: string[]): t.ConfigValue | undefined {
let cursor: unknown = obj;
for (const seg of segments) {
if (!cursor || typeof cursor !== 'object' || Array.isArray(cursor)) return undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/components/grants/CapabilityPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export function CapabilityPanel({ capabilities, onChange, disabled }: t.Capabili
v &&
(
CapabilityImplications[k as BaseSystemCapability] ?? []
).includes(cap),
).includes(cap as BaseSystemCapability),
)?.[0]
?.replace(/:/g, '_') ?? cap.replace(/:/g, '_')
}`,
Expand Down
2 changes: 2 additions & 0 deletions src/constants/role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const PERMISSION_TYPE_SCHEMA: Record<PermissionTypes, Permissions[]> = {
Permissions.CREATE,
Permissions.SHARE,
Permissions.SHARE_PUBLIC,
Permissions.CONFIGURE_OBO,
],
[PermissionTypes.REMOTE_AGENTS]: [
Permissions.USE,
Expand All @@ -53,6 +54,7 @@ export const PERMISSION_TYPE_SCHEMA: Record<PermissionTypes, Permissions[]> = {
Permissions.SHARE,
Permissions.SHARE_PUBLIC,
],
[PermissionTypes.SHARED_LINKS]: [Permissions.CREATE, Permissions.SHARE, Permissions.SHARE_PUBLIC],
};

export function defaultPermissions(): t.RolePermissions {
Expand Down
64 changes: 64 additions & 0 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,57 @@
"com_config_field_allowedAddresses_item": "address",
"com_config_field_includeDomains_item": "domain",
"com_config_field_excludeDomains_item": "domain",
"com_config_field_agent_ids": "Agent IDs",
"com_config_field_agent_ids_item": "agent ID",
"com_config_field_allowSelf": "Allow self",
"com_config_field_buildInfo": "Build info",
"com_config_field_cacheRead": "Cache read",
"com_config_field_cacheWrite": "Cache write",
"com_config_field_code": "Code",
"com_config_field_completion": "Completion",
"com_config_field_configureObo": "Configure on-behalf-of",
"com_config_field_contextCost": "Context cost",
"com_config_field_contextUsage": "Context usage",
"com_config_field_conversation_starters": "Conversation starters",
"com_config_field_conversation_starters_item": "starter",
"com_config_field_currency": "Currency",
"com_config_field_customPatterns": "Custom patterns",
"com_config_field_customPatterns_item": "pattern",
"com_config_field_forward_audience_on_refresh": "Forward audience on refresh",
"com_config_field_github": "GitHub",
"com_config_field_guardrailConfig": "Guardrail",
Comment thread
dustinhealy marked this conversation as resolved.
"com_config_field_guardrailIdentifier": "Guardrail identifier",
"com_config_field_guardrailVersion": "Guardrail version",
"com_config_field_hideBadgeRow": "Hide badge row",
"com_config_field_intervalMinutes": "Interval (minutes)",
"com_config_field_isTemporary": "Is temporary",
"com_config_field_maxCatalogSkills": "Max catalog skills",
"com_config_field_maxInputTokens": "Max input tokens",
"com_config_field_messageFilter": "Message filter",
"com_config_field_obo": "On-Behalf-Of",
Comment thread
dustinhealy marked this conversation as resolved.
"com_config_field_pii": "Personally Identifiable Information",
"com_config_field_proxy": "Proxy",
Comment thread
dustinhealy marked this conversation as resolved.
"com_config_field_rate": "Rate",
"com_config_field_reasoningFormat": "Reasoning format",
"com_config_field_reasoningKey": "Reasoning key",
"com_config_field_regex": "Regex",
"com_config_field_retainAgentFiles": "Retain agent files",
"com_config_field_retentionMode": "Retention mode",
"com_config_field_runOnStartup": "Run on startup",
"com_config_field_sensitive": "Sensitive",
"com_config_field_sharedLinks": "Shared links",
"com_config_field_showOnLanding": "Show on landing",
"com_config_field_skillSync": "Skill sync",
"com_config_field_softDefault": "Soft default",
"com_config_field_sources": "Sources",
"com_config_field_sources_item": "source",
"com_config_field_starterPatterns": "Starter patterns",
"com_config_field_starterPatterns_item": "pattern",
"com_config_field_streamProcessingMode": "Stream processing mode",
"com_config_field_subagents": "Subagents",
"com_config_field_titleTiming": "Title timing",
"com_config_field_tokenConfig": "Token configuration",
"com_config_field_trace": "Trace",
"com_config_section_fileStrategies": "File strategies",
"com_config_section_fileStrategies_desc": "Configure file handling strategies per endpoint",
"com_config_section_cloudfront": "CloudFront",
Expand All @@ -664,10 +715,16 @@
"com_config_section_imageOutputType_desc": "Format for generated image output",
"com_config_section_includedTools": "Included tools",
"com_config_section_includedTools_desc": "Tools explicitly included for availability",
"com_config_section_messageFilter": "Message filter",
"com_config_section_messageFilter_desc": "Filter or block patterns in user messages",
"com_config_section_secureImageLinks": "Secure image links",
"com_config_section_secureImageLinks_desc": "Enable secure, authenticated image URL delivery",
"com_config_section_skillSync": "Skill sync",
"com_config_section_skillSync_desc": "Periodically pull skill definitions from external sources",
"com_config_section_summarization": "Summarization",
"com_config_section_summarization_desc": "Configure conversation summarization behavior",
"com_endpoint_prompt_cache": "Prompt cache",
"com_endpoint_anthropic_prompt_cache": "Cache stable prompt context (system messages, tools, long inputs) at the API to reduce per-request input tokens on Anthropic models",
"com_auth_title": "Admin Panel",
"com_auth_email_label": "Email",
"com_auth_email_placeholder": "admin@example.com",
Expand Down Expand Up @@ -903,6 +960,7 @@
"com_perm_desc_MCP_SERVERS": "Connect and manage MCP tool servers",
"com_perm_desc_REMOTE_AGENTS": "Connect and manage remote agent endpoints",
"com_perm_desc_SKILLS": "Create, share, and use agent skills",
"com_perm_desc_SHARED_LINKS": "Create and share conversation share links",
"com_perm_desc_PEOPLE_PICKER": "Control visibility of users, groups, and roles",
"com_perm_type_BOOKMARKS": "Bookmarks",
"com_perm_type_PROMPTS": "Prompts",
Expand All @@ -919,6 +977,7 @@
"com_perm_type_MCP_SERVERS": "MCP servers",
"com_perm_type_REMOTE_AGENTS": "Remote agents",
"com_perm_type_SKILLS": "Skills",
"com_perm_type_SHARED_LINKS": "Shared links",
"com_perm_USE": "Use",
"com_perm_CREATE": "Create",
"com_perm_UPDATE": "Update",
Expand All @@ -930,6 +989,7 @@
"com_perm_VIEW_GROUPS": "View groups",
"com_perm_VIEW_ROLES": "View roles",
"com_perm_SHARE_PUBLIC": "Share publicly",
"com_perm_CONFIGURE_OBO": "Configure on-behalf-of",
"com_help_title": "Help",
"com_help_subtitle": "Documentation, guides, and support resources",
"com_help_resources": "Resources",
Expand Down Expand Up @@ -1000,6 +1060,8 @@
"com_cap_manage_prompts": "Manage prompts",
"com_cap_read_skills": "Read skills",
"com_cap_manage_skills": "Manage skills",
"com_cap_read_sharedlinks": "Read shared links",
"com_cap_manage_sharedlinks": "Manage shared links",
"com_cap_read_assistants": "Read assistants",
"com_cap_manage_assistants": "Manage assistants",
"com_cap_desc_access_admin": "Full admin panel access",
Expand All @@ -1020,6 +1082,8 @@
"com_cap_desc_manage_prompts": "Create, edit, and delete prompts",
"com_cap_desc_read_skills": "View skill configurations",
"com_cap_desc_manage_skills": "Create, edit, and delete skills",
"com_cap_desc_read_sharedlinks": "View shared conversation links",
"com_cap_desc_manage_sharedlinks": "Manage shared conversation links",
"com_cap_desc_read_assistants": "View assistant configurations",
"com_cap_desc_manage_assistants": "Create, edit, and delete assistants",
"com_access_denied_title": "Access denied",
Expand Down
4 changes: 4 additions & 0 deletions src/server/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,10 @@ const SAMPLE_OVERRIDES: Record<string, unknown> = {
'endpoints.agents.remoteApi.auth.oidc.issuer': 'https://example.com',
'endpoints.agents.remoteApi.auth.oidc.jwksUri': 'https://example.com/.well-known/jwks.json',
'summarization.trigger': { type: 'token_ratio', value: 0.5 },
'skillSync.github.intervalMinutes': 5,
'skillSync.github.sources': [
{ id: 'sample', owner: 'foo', repo: 'bar', paths: ['skills/'], credentialKey: 'sample-key' },
],
};

/** Generates a representative value that a given UI control would produce. */
Expand Down
34 changes: 17 additions & 17 deletions src/server/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { z } from 'zod';
import yaml from 'js-yaml';
import { queryOptions } from '@tanstack/react-query';
import { AppService } from '@librechat/data-schemas';
import { configSchema } from 'librechat-data-provider';
import { createServerFn } from '@tanstack/react-start';
import { SystemCapabilities } from '@librechat/data-schemas/capabilities';
Expand Down Expand Up @@ -652,22 +651,23 @@ export const parseImportedYaml = createServerFn({ method: 'POST' })
};
}

try {
const appConfig = await AppService({ config: result.data });
return { success: true, error: undefined, validationErrors: undefined, appConfig };
} catch (appServiceError) {
console.warn(
'AppService failed for imported config, falling back to raw config:',
appServiceError instanceof Error ? appServiceError.message : appServiceError,
);
const fallbackConfig = result.data;
return {
success: true,
error: undefined,
validationErrors: undefined,
appConfig: fallbackConfig,
};
}
/**
* `librechat-data-provider` and `@librechat/data-schemas` both migrated
* to tsdown (upstream #13578, #13597) and now ship dual `.d.cts` + `.d.mts`
* declaration files. Under `moduleResolution: bundler`, TS treats
* `TCustomConfig` resolved through one declaration path as nominally
* distinct from `TCustomConfig` resolved through the other, even when
* structurally identical. That collision shows up here as "Two different
* types with this name exist, but they are unrelated" in the ServerFn
* registration. The consumer (ImportYamlDialog) treats appConfig as
* `Record<string, ConfigValue>`, so widening the return is the local fix.
*/
return {
success: true,
error: undefined,
validationErrors: undefined,
appConfig: result.data as Record<string, t.ConfigValue>,
};
});

function getFieldDefault(schema: t.ZodSchemaLike): { hasDefault: boolean; value: unknown } {
Expand Down
3 changes: 0 additions & 3 deletions src/types/config-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,6 @@ export interface FieldRendererProps {
schemaDefaults?: FlatConfigMap;
showConfiguredOnly?: boolean;
isEditingScope?: boolean;
/** When true, never strip labels via isSoleField. Use when rendering a
* subset of fields within a larger section (e.g. priority fields). */
alwaysShowLabels?: boolean;
/** YAML-defined entry keys for the section being rendered. */
yamlBaseKeys?: Set<string>;
onValidationError?: (message: string) => void;
Expand Down