diff --git a/bun.lock b/bun.lock index 5cb0e1e..1f29249 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "tanstack-start-app", "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", @@ -25,7 +25,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", "lucide-react": "^0.545.0", "prom-client": "^15.1.3", "react": "^19.2.0", @@ -292,7 +292,7 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@librechat/data-schemas": ["@librechat/data-schemas@0.0.51", "", { "peerDependencies": { "jsonwebtoken": "^9.0.2", "klona": "^2.0.6", "librechat-data-provider": "*", "lodash": "^4.17.23", "meilisearch": "^0.38.0", "mongoose": "^8.23.1", "nanoid": "^3.3.7", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" } }, "sha512-EBfuq2NitbxgnoJZMXvH8FARvDkte8vNTfE+QDqpnktbM0ftvRqh6wxXQcq2JL50ED8ApuSewoni6gLVCVBwkw=="], + "@librechat/data-schemas": ["@librechat/data-schemas@0.0.53", "", { "peerDependencies": { "jsonwebtoken": "^9.0.2", "klona": "^2.0.6", "librechat-data-provider": "*", "lodash": "^4.17.23", "meilisearch": "^0.38.0", "mongoose": "^8.23.1", "nanoid": "^3.3.7", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" } }, "sha512-R3wcxa4wEd6xzlrh4WuJlMaN9G2hnrUTcRccHOnOnAJUjzhZwFS44+0GOT1/ReQD+SQVeRtQzU/IXuD5u8gRzg=="], "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.4.6", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g=="], @@ -1188,7 +1188,7 @@ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - "librechat-data-provider": ["librechat-data-provider@0.8.502", "", { "dependencies": { "axios": "^1.16.0", "dayjs": "^1.11.13", "js-yaml": "^4.1.1", "zod": "^3.22.4" }, "peerDependencies": { "@tanstack/react-query": "^4.28.0" } }, "sha512-t88K1SNZ/o/uSK/MyuMfxhLp4FJsf2shxpa855Lbu0jvtiM4HQ0Mau7GJfuJUzZff9ycOirVTc1xiKmyMl7HPA=="], + "librechat-data-provider": ["librechat-data-provider@0.8.505", "", { "dependencies": { "axios": "^1.16.0", "dayjs": "^1.11.13", "js-yaml": "^4.1.1", "zod": "^3.22.4" }, "peerDependencies": { "@tanstack/react-query": "^4.28.0" } }, "sha512-Bcy7i7H1YR5lGNd44s6Rwf6+ceCUY0npJkDjwdOyUWW63DMRzSEHPWkNAofAyPns4TLXfrCTmSKFGddD7mDkMw=="], "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], diff --git a/package.json b/package.json index a15c01d..4265814 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", "lucide-react": "^0.545.0", "prom-client": "^15.1.3", "react": "^19.2.0", diff --git a/src/components/access/RolePermissionsPanel.tsx b/src/components/access/RolePermissionsPanel.tsx index 20f8c72..4c31f52 100644 --- a/src/components/access/RolePermissionsPanel.tsx +++ b/src/components/access/RolePermissionsPanel.tsx @@ -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, diff --git a/src/components/configuration/FieldRenderer.tsx b/src/components/configuration/FieldRenderer.tsx index 0a33409..655cb65 100644 --- a/src/components/configuration/FieldRenderer.tsx +++ b/src/components/configuration/FieldRenderer.tsx @@ -1164,7 +1164,6 @@ export function FieldRenderer({ pendingResets, schemaDefaults, showConfiguredOnly, - alwaysShowLabels, }: t.FieldRendererProps) { const localize = useLocalize(); const values = @@ -1267,7 +1266,7 @@ export function FieldRenderer({ pendingResets={pendingResets} schemaDefaults={schemaDefaults} showConfiguredOnly={showConfiguredOnly} - isSoleField={!alwaysShowLabels && fields.length === 1 && groups.length === 1} + isSoleField={false} /> ); })} diff --git a/src/components/configuration/configMeta.ts b/src/components/configuration/configMeta.ts index 97c957c..2d93958 100644 --- a/src/components/configuration/configMeta.ts +++ b/src/components/configuration/configMeta.ts @@ -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', @@ -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', + }, }; /** Sections omitted from the UI entirely (legacy fields pending removal from the schema). */ diff --git a/src/components/configuration/sections/EndpointsRenderer.tsx b/src/components/configuration/sections/EndpointsRenderer.tsx index ec9b764..c382731 100644 --- a/src/components/configuration/sections/EndpointsRenderer.tsx +++ b/src/components/configuration/sections/EndpointsRenderer.tsx @@ -567,7 +567,7 @@ function ProviderSection({ {hasPrioritySplit ? ( <> - + {restChildren.length > 0 && ( = { 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> = { stdio: new Set(['command', 'args']), @@ -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); @@ -646,7 +652,6 @@ function CreateMcpServerDialog({ ); } - export function McpServersRenderer(props: t.FieldRendererProps) { const { fields, @@ -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 = isPlainObject(baseValue) ? baseValue : EMPTY_RECORD; + const baseRecord: Record = isPlainObject(baseValue) + ? baseValue + : EMPTY_RECORD; const editsByEntry = useMemo(() => { const map = new Map>(); @@ -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; } @@ -1038,7 +1043,9 @@ 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} @@ -1046,10 +1053,7 @@ const McpEntryRow = memo(function McpEntryRowImpl({ ); }); -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; diff --git a/src/components/grants/CapabilityPanel.tsx b/src/components/grants/CapabilityPanel.tsx index 77459cd..6da86c4 100644 --- a/src/components/grants/CapabilityPanel.tsx +++ b/src/components/grants/CapabilityPanel.tsx @@ -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, '_') }`, diff --git a/src/constants/role.ts b/src/constants/role.ts index 6ce305b..dd64c49 100644 --- a/src/constants/role.ts +++ b/src/constants/role.ts @@ -40,6 +40,7 @@ export const PERMISSION_TYPE_SCHEMA: Record = { Permissions.CREATE, Permissions.SHARE, Permissions.SHARE_PUBLIC, + Permissions.CONFIGURE_OBO, ], [PermissionTypes.REMOTE_AGENTS]: [ Permissions.USE, @@ -53,6 +54,7 @@ export const PERMISSION_TYPE_SCHEMA: Record = { Permissions.SHARE, Permissions.SHARE_PUBLIC, ], + [PermissionTypes.SHARED_LINKS]: [Permissions.CREATE, Permissions.SHARE, Permissions.SHARE_PUBLIC], }; export function defaultPermissions(): t.RolePermissions { diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 3f95eaa..9a52c19 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -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", + "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", + "com_config_field_pii": "Personally Identifiable Information", + "com_config_field_proxy": "Proxy", + "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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", diff --git a/src/server/config.test.ts b/src/server/config.test.ts index 3c34820..67a04a1 100644 --- a/src/server/config.test.ts +++ b/src/server/config.test.ts @@ -731,6 +731,10 @@ const SAMPLE_OVERRIDES: Record = { '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. */ diff --git a/src/server/config.ts b/src/server/config.ts index 274ce12..b46abc4 100644 --- a/src/server/config.ts +++ b/src/server/config.ts @@ -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'; @@ -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`, so widening the return is the local fix. + */ + return { + success: true, + error: undefined, + validationErrors: undefined, + appConfig: result.data as Record, + }; }); function getFieldDefault(schema: t.ZodSchemaLike): { hasDefault: boolean; value: unknown } { diff --git a/src/types/config-ui.ts b/src/types/config-ui.ts index 1503e02..3826199 100644 --- a/src/types/config-ui.ts +++ b/src/types/config-ui.ts @@ -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; onValidationError?: (message: string) => void;