🚦 fix: Restrict Programmatic Tool Execution Maps (#15105)

* fix: restrict programmatic tool execution maps

* chore: bump `@librechat/agents` to v3.6.10

* fix: honor live programmatic caller projections

* style: sort caller capability imports

* test: expect caller projection loader argument

* chore: bump agents sdk to v3.6.11

* refactor: use SDK caller projection type

* style: sort agent handler imports
This commit is contained in:
Danny Avila 2026-08-22 11:02:09 -04:00 committed by GitHub
parent d3e70159ca
commit 89494d45fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 456 additions and 37 deletions

View file

@ -43,6 +43,7 @@ const {
AGENT_EXPECTED_MCP_TOOLS_UNAVAILABLE,
isFatalAgentInitializationError,
resolveCodeExecutionContext,
resolveCallerCapabilityProjectionSnapshot,
} = require('@librechat/api');
const {
Time,
@ -1902,6 +1903,7 @@ async function loadAgentTools({
* @param {string} [params.agentResourceType] - Permission resource type for the authorized agent route
* @param {string[]} params.toolNames - Names of tools to load
* @param {Map} [params.toolRegistry] - Tool registry
* @param {unknown} [params.callerCapabilityProjection] - SDK-owned live caller projection
* @param {Record<string, import('@librechat/api').LCAvailableTools>} [params.mcpAvailableTools] - Run-scoped MCP tool definitions
* @param {import('@librechat/api').RequestScopedMCPConnectionStore} [params.requestScopedConnections] - Run-scoped MCP connections
* @param {Record<string, Record<string, string>>} [params.userMCPAuthMap] - User MCP auth map
@ -1922,6 +1924,7 @@ async function loadToolsForExecution({
agentResourceType,
toolNames,
toolRegistry,
callerCapabilityProjection,
backgroundToolNames,
intentToolNames,
mcpAvailableTools,
@ -1943,6 +1946,12 @@ async function loadToolsForExecution({
requestBody: runtimeRequestBody,
requestScopedConnections: mcpRequestScopedConnections,
};
const activeCallerCapabilities = resolveCallerCapabilityProjectionSnapshot(
callerCapabilityProjection,
);
const activeCodeExecutionToolNames = activeCallerCapabilities
? new Set(activeCallerCapabilities.codeExecutionToolNames)
: undefined;
/** Per-agent set of tools that received the injected `run_in_background`
* param; the event-driven executor gates background dispatch and the
* `check_background_task` poll tool on this reliable per-agent channel. */
@ -2104,9 +2113,14 @@ async function loadToolsForExecution({
let ptcOrchestratedToolNames = [];
if (isPTC && toolRegistry) {
ptcOrchestratedToolNames = Array.from(toolRegistry.keys()).filter(
(name) => !specialToolNames.has(name),
);
ptcOrchestratedToolNames = Array.from(toolRegistry.values())
.filter(
(toolDef) =>
!specialToolNames.has(toolDef.name) &&
(toolDef.allowed_callers ?? ['direct']).includes('code_execution') &&
(activeCodeExecutionToolNames == null || activeCodeExecutionToolNames.has(toolDef.name)),
)
.map((toolDef) => toolDef.name);
}
const requestedNonSpecialToolNames = toolNames.filter((name) => !specialToolNames.has(name));
@ -2199,7 +2213,9 @@ async function loadToolsForExecution({
if (
tool.name &&
tool.name !== AgentConstants.PROGRAMMATIC_TOOL_CALLING &&
tool.name !== AgentConstants.BASH_PROGRAMMATIC_TOOL_CALLING
tool.name !== AgentConstants.BASH_PROGRAMMATIC_TOOL_CALLING &&
(toolRegistry.get(tool.name)?.allowed_callers ?? ['direct']).includes('code_execution') &&
(activeCodeExecutionToolNames == null || activeCodeExecutionToolNames.has(tool.name))
) {
ptcToolMap.set(tool.name, tool);
}