diff --git a/api/package.json b/api/package.json index bb367399a7..21dd5caeff 100644 --- a/api/package.json +++ b/api/package.json @@ -49,7 +49,7 @@ "@langchain/google-vertexai": "^0.2.13", "@langchain/openai": "^0.5.18", "@langchain/textsplitters": "^0.1.0", - "@librechat/agents": "^3.0.0-rc6", + "@librechat/agents": "^3.0.0-rc7", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", diff --git a/client/src/components/Chat/Messages/Content/AgentHandoff.tsx b/client/src/components/Chat/Messages/Content/AgentHandoff.tsx new file mode 100644 index 0000000000..9fa6756326 --- /dev/null +++ b/client/src/components/Chat/Messages/Content/AgentHandoff.tsx @@ -0,0 +1,91 @@ +import React, { useMemo, useState } from 'react'; +import { EModelEndpoint, Constants } from 'librechat-data-provider'; +import { ChevronDown } from 'lucide-react'; +import type { TMessage } from 'librechat-data-provider'; +import MessageIcon from '~/components/Share/MessageIcon'; +import { useAgentsMapContext } from '~/Providers'; +import { useLocalize } from '~/hooks'; +import { cn } from '~/utils'; + +interface AgentHandoffProps { + name: string; + args: string | Record; + output?: string | null; +} + +const AgentHandoff: React.FC = ({ name, args: _args = '' }) => { + const localize = useLocalize(); + const agentsMap = useAgentsMapContext(); + const [showInfo, setShowInfo] = useState(false); + + /** Extracted agent ID from tool name (e.g., "lc_transfer_to_agent_gUV0wMb7zHt3y3Xjz-8_4" -> "agent_gUV0wMb7zHt3y3Xjz-8_4") */ + const targetAgentId = useMemo(() => { + if (typeof name !== 'string' || !name.startsWith(Constants.LC_TRANSFER_TO_)) { + return null; + } + return name.replace(Constants.LC_TRANSFER_TO_, ''); + }, [name]); + + const targetAgent = useMemo(() => { + if (!targetAgentId || !agentsMap) { + return null; + } + return agentsMap[targetAgentId]; + }, [agentsMap, targetAgentId]); + + const args = useMemo(() => { + if (typeof _args === 'string') { + return _args; + } + try { + return JSON.stringify(_args, null, 2); + } catch { + return ''; + } + }, [_args]) as string; + + const hasInfo = useMemo(() => (args?.length ?? 0) > 0, [args]); + + return ( +
+
hasInfo && setShowInfo(!showInfo)} + > +
+ +
+ {localize('com_ui_transferred_to')} + + {targetAgent?.name || localize('com_ui_agent')} + + {hasInfo && ( + + )} +
+ {hasInfo && showInfo && ( +
+
+ {localize('com_ui_handoff_instructions')}: +
+
{args}
+
+ )} +
+ ); +}; + +export default AgentHandoff; diff --git a/client/src/components/Chat/Messages/Content/Part.tsx b/client/src/components/Chat/Messages/Content/Part.tsx index 75e6d6ea17..5526be1044 100644 --- a/client/src/components/Chat/Messages/Content/Part.tsx +++ b/client/src/components/Chat/Messages/Content/Part.tsx @@ -1,5 +1,6 @@ import { Tools, + Constants, ContentTypes, ToolCallTypes, imageGenTools, @@ -10,6 +11,7 @@ import type { TMessageContentParts, TAttachment } from 'librechat-data-provider' import { OpenAIImageGen, EmptyText, Reasoning, ExecuteCode, AgentUpdate, Text } from './Parts'; import { ErrorMessage } from './MessageContent'; import RetrievalCall from './RetrievalCall'; +import AgentHandoff from './AgentHandoff'; import CodeAnalyze from './CodeAnalyze'; import Container from './Container'; import WebSearch from './WebSearch'; @@ -118,6 +120,14 @@ const Part = memo( isLast={isLast} /> ); + } else if (isToolCall && toolCall.name?.startsWith(Constants.LC_TRANSFER_TO_)) { + return ( + + ); } else if (isToolCall) { return ( = ({ currentAgentId }) => { const localize = useLocalize(); - const agentsMap = useAgentsMapContext() || {}; - const currentAgent = useMemo(() => agentsMap[currentAgentId], [agentsMap, currentAgentId]); + const agentsMap = useAgentsMapContext(); + const currentAgent = useMemo(() => agentsMap?.[currentAgentId], [agentsMap, currentAgentId]); if (!currentAgentId) { return null; } diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json index 4b710ce716..2fcfdf4c0d 100644 --- a/client/src/locales/en/translation.json +++ b/client/src/locales/en/translation.json @@ -664,6 +664,10 @@ "com_ui_agent_handoff_prompt_placeholder": "Tell this agent what content to generate and pass to the handoff agent. You need to add something here to enable this feature", "com_ui_agent_handoff_prompt_key": "Content parameter name (default: 'instructions')", "com_ui_agent_handoff_prompt_key_placeholder": "Label the content passed (default: 'instructions')", + "com_ui_transferring_to_agent": "Transferring to {{0}}", + "com_ui_transferred_to_agent": "Transferred to {{0}}", + "com_ui_transferred_to": "Transferred to", + "com_ui_handoff_instructions": "Handoff instructions", "com_ui_agent_delete_error": "There was an error deleting the agent", "com_ui_agent_deleted": "Successfully deleted agent", "com_ui_agent_duplicate_error": "There was an error duplicating the agent", diff --git a/package-lock.json b/package-lock.json index b0ac2a4887..cd100e8aa4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,7 +64,7 @@ "@langchain/google-vertexai": "^0.2.13", "@langchain/openai": "^0.5.18", "@langchain/textsplitters": "^0.1.0", - "@librechat/agents": "^3.0.0-rc6", + "@librechat/agents": "^3.0.0-rc7", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", @@ -21909,9 +21909,9 @@ } }, "node_modules/@librechat/agents": { - "version": "3.0.0-rc6", - "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.0.0-rc6.tgz", - "integrity": "sha512-MAE+HdoRw/XKWIzhoYOUiJrPjN6xicOiLRlDarYAZe4JewLKV2MuBGhRJW9TCn0kwyvGJsMQkTX8xQIXZw7OuA==", + "version": "3.0.0-rc7", + "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.0.0-rc7.tgz", + "integrity": "sha512-cwXAr6c9knEglcMLuGv4FYQ4U4kJM2jvnyjHxwYRA7JYmaALKi7D5X1NMstWDUrLCOK1UIxGqtEagcUJu6mvCA==", "license": "MIT", "dependencies": { "@langchain/anthropic": "^0.3.26", @@ -51984,7 +51984,7 @@ }, "peerDependencies": { "@langchain/core": "^0.3.72", - "@librechat/agents": "^3.0.0-rc6", + "@librechat/agents": "^3.0.0-rc7", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.17.1", "axios": "^1.8.2", diff --git a/packages/api/package.json b/packages/api/package.json index 3923f03d59..a233d5153d 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -74,7 +74,7 @@ }, "peerDependencies": { "@langchain/core": "^0.3.72", - "@librechat/agents": "^3.0.0-rc6", + "@librechat/agents": "^3.0.0-rc7", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.17.1", "axios": "^1.8.2", diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index 66b6170cd9..167ac8f01f 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -1566,6 +1566,10 @@ export enum Constants { * This helps inform the UI if the mcp server was previously added. * */ mcp_server = 'sys__server__sys', + /** + * Handoff Tool Name Prefix + */ + LC_TRANSFER_TO_ = 'lc_transfer_to_', /** Placeholder Agent ID for Ephemeral Agents */ EPHEMERAL_AGENT_ID = 'ephemeral', }