)}
- {item.kind === 'action' && item.endpointCount > 0 && (
-
-
- {localize(
- item.endpointCount === 1
- ? 'com_ui_tools_endpoint_count_one'
- : 'com_ui_tools_endpoint_count',
- { count: item.endpointCount },
- )}
-
+ {(item.kind === 'action' && item.endpointCount > 0) || isPublicSkill || isSharedSkill ? (
+
+ {item.kind === 'action' && item.endpointCount > 0 && (
+
+ {localize(
+ item.endpointCount === 1
+ ? 'com_ui_tools_endpoint_count_one'
+ : 'com_ui_tools_endpoint_count',
+ { count: item.endpointCount },
+ )}
+
+ )}
+ {isSharedSkill && skill && (
+
+
+ {skill.authorName}
+
+ )}
+ {isPublicSkill && (
+
+
+
+ )}
- )}
+ ) : null}
- {canConfigure && (
+ {(canConfigure || showInfoOnly) && (
)}
diff --git a/client/src/components/SidePanel/Agents/Tools/ToolsMarketplaceDialog.tsx b/client/src/components/SidePanel/Agents/Tools/ToolsMarketplaceDialog.tsx
index 803fb3ab68..2327b4bc30 100644
--- a/client/src/components/SidePanel/Agents/Tools/ToolsMarketplaceDialog.tsx
+++ b/client/src/components/SidePanel/Agents/Tools/ToolsMarketplaceDialog.tsx
@@ -12,7 +12,12 @@ import {
import type { AgentItem, AgentItemKind, ItemFilter } from './items/types';
import type { TranslationKeys } from '~/hooks/useLocalize';
import type { AgentForm } from '~/common';
-import { useBuiltinAuthMap, useShowMemory, useUninstallToolCredentials } from './hooks';
+import {
+ useBuiltinAuthMap,
+ useShowMemory,
+ useWebSearchUserProvided,
+ useUninstallToolCredentials,
+} from './hooks';
import AddMcpServerDialog from './ItemDialog/AddMcpServerDialog';
import { deriveSelectedItems, itemKey } from './items/selectors';
import { computeToggleAction } from './items/mutations';
@@ -52,6 +57,7 @@ export default function ToolsMarketplaceDialog({
});
const builtinAuthMap = useBuiltinAuthMap();
const showMemory = useShowMemory();
+ const webSearchUserProvided = useWebSearchUserProvided();
const uninstallToolCredentials = useUninstallToolCredentials();
const { data: favorites } = useGetFavoritesQuery();
@@ -115,6 +121,7 @@ export default function ToolsMarketplaceDialog({
actions: agentActions,
permissions: { mcp: hasMcpAccess, skills: false },
showMemory,
+ webSearchUserProvided,
builtinAuthMap,
}),
[
@@ -124,6 +131,7 @@ export default function ToolsMarketplaceDialog({
agentActions,
hasMcpAccess,
showMemory,
+ webSearchUserProvided,
builtinAuthMap,
],
);
diff --git a/client/src/components/SidePanel/Agents/Tools/ToolsSection.tsx b/client/src/components/SidePanel/Agents/Tools/ToolsSection.tsx
index 15d72ac3d3..1f6d5bcc72 100644
--- a/client/src/components/SidePanel/Agents/Tools/ToolsSection.tsx
+++ b/client/src/components/SidePanel/Agents/Tools/ToolsSection.tsx
@@ -6,7 +6,12 @@ import { PermissionTypes, Permissions, AgentCapabilities } from 'librechat-data-
import type { TPlugin } from 'librechat-data-provider';
import type { AgentItem } from './items/types';
import type { AgentForm } from '~/common';
-import { useBuiltinAuthMap, useShowMemory, useUninstallToolCredentials } from './hooks';
+import {
+ useBuiltinAuthMap,
+ useShowMemory,
+ useWebSearchUserProvided,
+ useUninstallToolCredentials,
+} from './hooks';
import { useListSkillsQuery, useDeleteAgentAction } from '~/data-provider';
import { useRemoveMCPTool, useVisibleTools } from '~/hooks/MCP';
import ToolsMarketplaceDialog from './ToolsMarketplaceDialog';
@@ -66,6 +71,7 @@ export default function ToolsSection({ agentId }: Props) {
const { data: skillsData } = useListSkillsQuery({ limit: 100 }, { enabled: showSkills });
const showMemory = useShowMemory();
+ const webSearchUserProvided = useWebSearchUserProvided();
const builtinAuthMap = useBuiltinAuthMap();
const uninstallToolCredentials = useUninstallToolCredentials();
@@ -97,6 +103,7 @@ export default function ToolsSection({ agentId }: Props) {
actions: agentActions,
permissions: { mcp: hasMcpAccess, skills: showSkills },
showMemory,
+ webSearchUserProvided,
builtinAuthMap,
}),
[
@@ -108,6 +115,7 @@ export default function ToolsSection({ agentId }: Props) {
hasMcpAccess,
showSkills,
showMemory,
+ webSearchUserProvided,
builtinAuthMap,
],
);
diff --git a/client/src/components/SidePanel/Agents/Tools/__tests__/MarketplaceCatalog.spec.tsx b/client/src/components/SidePanel/Agents/Tools/__tests__/MarketplaceCatalog.spec.tsx
index c53531c000..f2ba35325b 100644
--- a/client/src/components/SidePanel/Agents/Tools/__tests__/MarketplaceCatalog.spec.tsx
+++ b/client/src/components/SidePanel/Agents/Tools/__tests__/MarketplaceCatalog.spec.tsx
@@ -1,11 +1,12 @@
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, screen } from '@testing-library/react';
-import MarketplaceCatalog from '../MarketplaceCatalog';
import type { AgentItem } from '../items/types';
+import MarketplaceCatalog from '../MarketplaceCatalog';
import { itemKey } from '../items/selectors';
jest.mock('~/hooks', () => ({
useLocalize: () => (key: string) => key,
+ useAuthContext: () => ({ user: { id: 'u1' } }),
}));
const items: AgentItem[] = [
diff --git a/client/src/components/SidePanel/Agents/Tools/__tests__/ToolCard.spec.tsx b/client/src/components/SidePanel/Agents/Tools/__tests__/ToolCard.spec.tsx
index 1707cce631..74eb0be9ad 100644
--- a/client/src/components/SidePanel/Agents/Tools/__tests__/ToolCard.spec.tsx
+++ b/client/src/components/SidePanel/Agents/Tools/__tests__/ToolCard.spec.tsx
@@ -1,7 +1,7 @@
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, screen } from '@testing-library/react';
-import ToolCard from '../ToolCard';
import type { AgentItem } from '../items/types';
+import ToolCard from '../ToolCard';
jest.mock('~/hooks', () => ({
useLocalize:
@@ -11,6 +11,7 @@ jest.mock('~/hooks', () => ({
const parts = Object.entries(options).map(([name, value]) => `${name}=${String(value)}`);
return `${key}[${parts.join(',')}]`;
},
+ useAuthContext: () => ({ user: { id: 'u1' } }),
}));
const skill: AgentItem = {
@@ -69,4 +70,37 @@ describe('ToolCard', () => {
render(
);
expect(screen.queryByText('com_ui_tools_count[count=14]')).not.toBeInTheDocument();
});
+
+ test('renders public and shared-author badges for another user public skill', () => {
+ const shared: AgentItem = {
+ kind: 'skill',
+ id: 's2',
+ name: 'Shared',
+ description: '',
+ iconKey: 'skill',
+ skill: {
+ _id: 's2',
+ name: 'Shared',
+ author: 'u2',
+ authorName: 'Alice',
+ isPublic: true,
+ } as never,
+ };
+ render(
);
+ expect(screen.getByText('Alice')).toBeInTheDocument();
+ expect(screen.getByLabelText('com_ui_sr_public_skill')).toBeInTheDocument();
+ });
+
+ test('omits the author badge for the current user own skill', () => {
+ const own: AgentItem = {
+ kind: 'skill',
+ id: 's3',
+ name: 'Mine',
+ description: '',
+ iconKey: 'skill',
+ skill: { _id: 's3', name: 'Mine', author: 'u1', authorName: 'Me', isPublic: false } as never,
+ };
+ render(
);
+ expect(screen.queryByText('Me')).not.toBeInTheDocument();
+ });
});
diff --git a/client/src/components/SidePanel/Agents/Tools/__tests__/ToolsMarketplaceDialog.spec.tsx b/client/src/components/SidePanel/Agents/Tools/__tests__/ToolsMarketplaceDialog.spec.tsx
index 71a80b7155..24a89de7a8 100644
--- a/client/src/components/SidePanel/Agents/Tools/__tests__/ToolsMarketplaceDialog.spec.tsx
+++ b/client/src/components/SidePanel/Agents/Tools/__tests__/ToolsMarketplaceDialog.spec.tsx
@@ -60,6 +60,7 @@ jest.mock('react-router-dom', () => ({
jest.mock('../hooks', () => ({
useBuiltinAuthMap: () => new Map(),
useShowMemory: () => false,
+ useWebSearchUserProvided: () => false,
useUninstallToolCredentials: () => jest.fn(),
}));
diff --git a/client/src/components/SidePanel/Agents/Tools/__tests__/ToolsSection.spec.tsx b/client/src/components/SidePanel/Agents/Tools/__tests__/ToolsSection.spec.tsx
index 2177893b77..a5f3add5c0 100644
--- a/client/src/components/SidePanel/Agents/Tools/__tests__/ToolsSection.spec.tsx
+++ b/client/src/components/SidePanel/Agents/Tools/__tests__/ToolsSection.spec.tsx
@@ -39,6 +39,7 @@ jest.mock('~/hooks/MCP', () => ({
jest.mock('../hooks', () => ({
useBuiltinAuthMap: () => new Map(),
useShowMemory: () => false,
+ useWebSearchUserProvided: () => false,
useUninstallToolCredentials: () => jest.fn(),
}));
diff --git a/client/src/components/SidePanel/Agents/Tools/hooks.ts b/client/src/components/SidePanel/Agents/Tools/hooks.ts
index f62a6fa050..c0713e713f 100644
--- a/client/src/components/SidePanel/Agents/Tools/hooks.ts
+++ b/client/src/components/SidePanel/Agents/Tools/hooks.ts
@@ -27,6 +27,21 @@ export function useBuiltinAuthMap(): Map
{
}, [data]);
}
+/**
+ * Whether `web_search` uses USER_PROVIDED auth (a user-managed key). When false
+ * the deployment uses SYSTEM_DEFINED keys, so there is nothing for the user to
+ * configure. Shares the `useBuiltinAuthMap` React Query key, so it adds no
+ * request. Threaded into `buildCatalog` so the card/row affordance is decided
+ * synchronously (cog vs info) without a per-row hook.
+ */
+export function useWebSearchUserProvided(): boolean {
+ const { data } = useVerifyAgentToolAuth({ toolId: Tools.web_search }, { retry: 1 });
+ return useMemo(
+ () => data?.authTypes?.some(([, authType]) => authType === AuthType.USER_PROVIDED) ?? false,
+ [data],
+ );
+}
+
/**
* Returns a callback that revokes a tool's stored user credentials when it is
* removed from an agent, mirroring the legacy `AgentTool` removal. Without it,
diff --git a/client/src/components/SidePanel/Agents/Tools/items/__tests__/catalog.spec.ts b/client/src/components/SidePanel/Agents/Tools/items/__tests__/catalog.spec.ts
index 6a2d984f62..05e4272a97 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/__tests__/catalog.spec.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/__tests__/catalog.spec.ts
@@ -37,6 +37,21 @@ describe('buildCatalog', () => {
expect(buildCatalog({ ...emptyInputs, showMemory: true }).find(memoryId)).toBeDefined();
});
+ test('flags web_search userProvidedAuth from the webSearchUserProvided input', () => {
+ const findWebSearch = (inputs: BuildCatalogInputs) =>
+ buildCatalog(inputs).find(
+ (i) => i.kind === 'builtin' && i.id === AgentCapabilities.web_search,
+ );
+ const base = {
+ ...emptyInputs,
+ agentsConfig: { capabilities: [AgentCapabilities.web_search] },
+ };
+ const userProvided = findWebSearch({ ...base, webSearchUserProvided: true });
+ const systemDefined = findWebSearch({ ...base, webSearchUserProvided: false });
+ expect(userProvided?.kind === 'builtin' && userProvided.userProvidedAuth).toBe(true);
+ expect(systemDefined?.kind === 'builtin' && systemDefined.userProvidedAuth).toBe(false);
+ });
+
test('hides MCP items when the user lacks MCP permission', () => {
const map = new Map();
map.set('srv', { serverName: 'srv', isConfigured: true, tools: [] });
diff --git a/client/src/components/SidePanel/Agents/Tools/items/__tests__/configurable.spec.ts b/client/src/components/SidePanel/Agents/Tools/items/__tests__/configurable.spec.ts
new file mode 100644
index 0000000000..aa12b7c3d9
--- /dev/null
+++ b/client/src/components/SidePanel/Agents/Tools/items/__tests__/configurable.spec.ts
@@ -0,0 +1,82 @@
+import type { AgentItem } from '../types';
+import { makePlugin, makeMcpServer, makeSkill, makeAction } from 'test/itemFactories';
+import { hasConfigurableSettings } from '../configurable';
+
+const builtin = (id: string, extra: Record = {}): AgentItem =>
+ ({ kind: 'builtin', id, name: '', description: '', iconKey: id, ...extra }) as AgentItem;
+
+describe('hasConfigurableSettings', () => {
+ test('artifacts, file_search, and context builtins are configurable', () => {
+ expect(hasConfigurableSettings(builtin('artifacts'))).toBe(true);
+ expect(hasConfigurableSettings(builtin('file_search'))).toBe(true);
+ expect(hasConfigurableSettings(builtin('context'))).toBe(true);
+ });
+
+ test('execute_code and memory builtins are not configurable', () => {
+ expect(hasConfigurableSettings(builtin('execute_code'))).toBe(false);
+ expect(hasConfigurableSettings(builtin('memory'))).toBe(false);
+ });
+
+ test('web_search is configurable only when auth is user-provided', () => {
+ expect(hasConfigurableSettings(builtin('web_search'))).toBe(false);
+ expect(hasConfigurableSettings(builtin('web_search', { userProvidedAuth: false }))).toBe(false);
+ expect(hasConfigurableSettings(builtin('web_search', { userProvidedAuth: true }))).toBe(true);
+ });
+
+ test('mcp and action are always configurable; skills never are', () => {
+ const mcp: AgentItem = {
+ kind: 'mcp',
+ id: 'srv',
+ name: 'srv',
+ description: '',
+ iconKey: 'mcp',
+ server: makeMcpServer({ serverName: 'srv' }),
+ toolCount: 0,
+ };
+ const action: AgentItem = {
+ kind: 'action',
+ id: 'a1',
+ name: 'a1',
+ description: '',
+ iconKey: 'action',
+ action: makeAction({ action_id: 'a1' }),
+ endpointCount: 0,
+ };
+ const skill: AgentItem = {
+ kind: 'skill',
+ id: 's1',
+ name: 's1',
+ description: '',
+ iconKey: 'skill',
+ skill: makeSkill({ _id: 's1' }),
+ };
+ expect(hasConfigurableSettings(mcp)).toBe(true);
+ expect(hasConfigurableSettings(action)).toBe(true);
+ expect(hasConfigurableSettings(skill)).toBe(false);
+ });
+
+ test('a regular tool is configurable only when it needs auth', () => {
+ const noAuth: AgentItem = {
+ kind: 'tool',
+ id: 'dalle',
+ name: 'DALL-E',
+ description: '',
+ iconKey: 'tool',
+ plugin: makePlugin({ pluginKey: 'dalle' }),
+ };
+ const needsAuth: AgentItem = {
+ kind: 'tool',
+ id: 'serpapi',
+ name: 'SerpApi',
+ description: '',
+ iconKey: 'tool',
+ plugin: makePlugin({
+ pluginKey: 'serpapi',
+ authConfig: [{ authField: 'SERPAPI_API_KEY', label: 'Key', description: '' }],
+ authenticated: false,
+ }),
+ };
+ expect(hasConfigurableSettings(noAuth)).toBe(false);
+ expect(hasConfigurableSettings(needsAuth)).toBe(true);
+ });
+});
diff --git a/client/src/components/SidePanel/Agents/Tools/items/catalog.ts b/client/src/components/SidePanel/Agents/Tools/items/catalog.ts
index 6a66f8e2dc..f0f8b6f4b2 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/catalog.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/catalog.ts
@@ -45,6 +45,12 @@ export interface BuildCatalogInputs {
* `agentsConfig.capabilities` here.
*/
showMemory?: boolean;
+ /**
+ * Whether `web_search` uses USER_PROVIDED auth (a user-managed key). Drives the
+ * cog-vs-info affordance on the web_search card/row: configurable only when a
+ * user key exists; SYSTEM_DEFINED deployments have nothing to configure.
+ */
+ webSearchUserProvided?: boolean;
}
interface BuiltinDef {
@@ -107,6 +113,8 @@ export function buildCatalog(inputs: BuildCatalogInputs): AgentItem[] {
name: def.nameKey,
description: def.descriptionKey,
status: inputs.builtinAuthMap?.get(def.id) === true ? 'needs_setup' : undefined,
+ userProvidedAuth:
+ def.id === AgentCapabilities.web_search ? inputs.webSearchUserProvided === true : undefined,
});
}
diff --git a/client/src/components/SidePanel/Agents/Tools/items/configurable.ts b/client/src/components/SidePanel/Agents/Tools/items/configurable.ts
index 7eb938def5..07bbdfecb6 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/configurable.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/configurable.ts
@@ -10,7 +10,12 @@ import { pluginNeedsAuth } from './auth';
export function hasConfigurableSettings(item: AgentItem): boolean {
switch (item.kind) {
case 'builtin':
- return item.id === 'artifacts' || item.id === 'file_search' || item.id === 'context';
+ return (
+ item.id === 'artifacts' ||
+ item.id === 'file_search' ||
+ item.id === 'context' ||
+ (item.id === 'web_search' && item.userProvidedAuth === true)
+ );
case 'tool':
return pluginNeedsAuth(item.plugin);
case 'mcp':
diff --git a/client/src/components/SidePanel/Agents/Tools/items/types.ts b/client/src/components/SidePanel/Agents/Tools/items/types.ts
index 09975ffac4..9252be0305 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/types.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/types.ts
@@ -36,6 +36,12 @@ interface ItemBase {
export interface BuiltinItem extends ItemBase {
kind: 'builtin';
id: BuiltinId;
+ /**
+ * True when `web_search` auth is USER_PROVIDED (a user-managed key exists to
+ * configure). Undefined/false means SYSTEM_DEFINED — nothing to configure, so
+ * the card/row shows an info icon instead of a settings cog.
+ */
+ userProvidedAuth?: boolean;
}
export interface ToolItem extends ItemBase {
diff --git a/client/src/components/Skills/dialogs/CreateSkillDialog.tsx b/client/src/components/Skills/dialogs/CreateSkillDialog.tsx
index ed8041423d..3b03c29ad8 100644
--- a/client/src/components/Skills/dialogs/CreateSkillDialog.tsx
+++ b/client/src/components/Skills/dialogs/CreateSkillDialog.tsx
@@ -12,6 +12,7 @@ import {
SKILL_NAME_MAX_LENGTH,
SKILL_DESCRIPTION_MAX_LENGTH,
} from 'librechat-data-provider';
+import type { TSkill } from 'librechat-data-provider';
import { useCreateSkillMutation } from '~/data-provider';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
@@ -22,6 +23,12 @@ interface CreateSkillDialogProps {
defaultName?: string;
defaultDescription?: string;
defaultBody?: string;
+ /**
+ * Called with the created skill instead of navigating to its page. Lets the
+ * dialog be reused in-context (e.g. the agent-builder skill picker) so creating
+ * a skill keeps the user in place rather than routing to `/skills/:id`.
+ */
+ onCreated?: (skill: TSkill) => void;
}
interface FormValues {
@@ -40,6 +47,7 @@ export default function CreateSkillDialog({
defaultName = '',
defaultDescription = '',
defaultBody = '',
+ onCreated,
}: CreateSkillDialogProps) {
const localize = useLocalize();
const navigate = useNavigate();
@@ -60,6 +68,10 @@ export default function CreateSkillDialog({
showToast({ status: 'success', message: localize('com_ui_skill_created') });
setIsOpen(false);
reset();
+ if (onCreated) {
+ onCreated(skill);
+ return;
+ }
navigate(`/skills/${skill._id}`);
},
onError: (error: unknown) => {
diff --git a/client/src/components/Tools/MCPToolItem.tsx b/client/src/components/Tools/MCPToolItem.tsx
deleted file mode 100644
index 7e9012e8ce..0000000000
--- a/client/src/components/Tools/MCPToolItem.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { XCircle, PlusCircleIcon, Wrench } from 'lucide-react';
-import type { AgentToolType } from 'librechat-data-provider';
-import { useLocalize } from '~/hooks';
-
-type MCPToolItemProps = {
- tool: AgentToolType;
- onAddTool: () => void;
- onRemoveTool: () => void;
- isInstalled?: boolean;
- isConfiguring?: boolean;
- isInitializing?: boolean;
-};
-
-function MCPToolItem({
- tool,
- onAddTool,
- onRemoveTool,
- isInstalled = false,
- isConfiguring = false,
- isInitializing = false,
-}: MCPToolItemProps) {
- const localize = useLocalize();
- const handleClick = () => {
- if (isInstalled) {
- onRemoveTool();
- } else {
- onAddTool();
- }
- };
-
- const name = tool.metadata?.name || tool.tool_id;
- const description = tool.metadata?.description || '';
- const icon = tool.metadata?.icon;
-
- // Determine button state and text
- const getButtonState = () => {
- if (isInstalled) {
- return {
- text: localize('com_nav_tool_remove'),
- icon: ,
- className: 'btn btn-neutral border-token-border-light relative',
- disabled: false,
- };
- }
-
- if (isConfiguring) {
- return {
- text: localize('com_ui_confirm'),
- icon: ,
- className: 'btn btn-primary relative',
- disabled: false,
- };
- }
-
- if (isInitializing) {
- return {
- text: localize('com_ui_initializing'),
- icon: ,
- className: 'btn btn-primary relative opacity-75 cursor-not-allowed',
- disabled: true,
- };
- }
-
- return {
- text: localize('com_ui_add'),
- icon: ,
- className: 'btn btn-primary relative',
- disabled: false,
- };
- };
-
- const buttonState = getButtonState();
-
- return (
-
-
-
-
- {icon ? (
-

- ) : (
-
-
-
- )}
-
-
-
-
-
- {name}
-
-
-
-
-
{description}
-
- );
-}
-
-export default MCPToolItem;
diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json
index 5660832196..9aaf80c46c 100644
--- a/client/src/locales/en/translation.json
+++ b/client/src/locales/en/translation.json
@@ -1269,7 +1269,6 @@
"com_ui_import_conversation_upload_error": "Error uploading file. Please try again.",
"com_ui_importing": "Importing",
"com_ui_include_shadcnui": "Include shadcn/ui components instructions",
- "com_ui_initializing": "Initializing...",
"com_ui_input": "Input",
"com_ui_instructions": "Instructions",
"com_ui_invalid_json": "Invalid JSON",
@@ -1327,6 +1326,8 @@
"com_ui_mcp_invalid_url": "Please enter a valid URL",
"com_ui_mcp_no_description": "No description available",
"com_ui_mcp_oauth_cancelled": "OAuth login cancelled for {{0}}",
+ "com_ui_mcp_oauth_qr_code_description": "QR code to open the OAuth login on another device",
+ "com_ui_mcp_oauth_scan_qr": "Scan to open on your phone",
"com_ui_mcp_oauth_timeout": "OAuth login timed out for {{0}}",
"com_ui_mcp_programmatic": "Programmatic",
"com_ui_mcp_programmatic_all": "Mark all as programmatic",
@@ -1930,6 +1931,7 @@
"com_ui_tools_search_no_results": "Nothing matched your search",
"com_ui_tools_section_count": "{{count}} enabled",
"com_ui_tools_section_title": "Tools",
+ "com_ui_tools_shared_by": "Shared by {{name}}",
"com_ui_tools_skills_enabled_kill_switch": "Agent skills",
"com_ui_tools_skills_enabled_kill_switch_hint": "Master switch for this agent's skills. When off, the agent ignores every skill you've selected, letting you disable them all at once without clearing your selection.",
"com_ui_tools_source_action": "Custom action",
@@ -1953,7 +1955,6 @@
"com_ui_unarchive_conversation": "Unarchive conversation",
"com_ui_unarchive_error": "Failed to unarchive conversation",
"com_ui_unassigned": "Unassigned",
- "com_ui_unavailable": "Unavailable",
"com_ui_unfavorite": "Remove from favorites",
"com_ui_unknown": "Unknown",
"com_ui_unknown_file_type": "Unknown file type",