🛻 feat: Route Bash Through Attached Workspaces (#15546)

* feat(code): Route bash through attached workspaces

* fix(code): harden attached bash schema adapter
This commit is contained in:
Danny Avila 2026-09-04 11:49:51 -04:00 • committed by GitHub
parent 2656f44c03
commit fddf7e31f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 485 additions and 18 deletions

View file

@ -45,6 +45,7 @@ const {
AGENT_EXPECTED_MCP_TOOLS_UNAVAILABLE,
isFatalAgentInitializationError,
codeExecutionAuthHeaders,
createAttachedWorkspaceBashTool,
resolveCodeExecutionContext,
resolveCallerCapabilityProjectionSnapshot,
LIST_WORKSPACE_FILES_TOOL_NAME,
@ -2122,14 +2123,21 @@ async function loadToolsForExecution({
}
if (isBashTool) {
try {
const bashTool = createBashExecutionTool({
authHeaders: () =>
codeExecutionAuthHeaders(
(bridgeWorkerId) => getCodeApiAuthHeaders(req, bridgeWorkerId),
codeExecutionContext,
),
...codeExecutionContext,
});
const authHeaders = () =>
codeExecutionAuthHeaders(
(bridgeWorkerId) => getCodeApiAuthHeaders(req, bridgeWorkerId),
codeExecutionContext,
);
const bashTool =
codeExecutionContext.environmentType === 'attached'
? createAttachedWorkspaceBashTool({
authHeaders,
baseUrl: codeExecutionContext.baseUrl,
})
: createBashExecutionTool({
authHeaders,
...codeExecutionContext,
});
allLoadedTools.push(bashTool);
} catch (error) {
logger.error(

View file

@ -18,6 +18,7 @@ const mockGetMCPServerTools = jest.fn();
const mockGetCachedTools = jest.fn();
const mockSendEvent = jest.fn();
const mockEmitChunk = jest.fn();
const mockCreateAttachedWorkspaceBashTool = jest.fn(() => ({ name: AgentConstants.BASH_TOOL }));
const mockResolveCodeExecutionContext = jest.fn(
({ statefulSessions, environment, userId, agentId, conversationId }) => {
if (!statefulSessions) {
@ -80,6 +81,7 @@ jest.mock('@librechat/api', () => ({
emitChunk: (...args) => mockEmitChunk(...args),
},
resolveCodeExecutionContext: (...args) => mockResolveCodeExecutionContext(...args),
createAttachedWorkspaceBashTool: (...args) => mockCreateAttachedWorkspaceBashTool(...args),
}));
const mockLoadToolsUtil = jest.fn();
@ -2631,6 +2633,47 @@ describe('ToolService - Action Capability Gating', () => {
}
});
it('loads bash execution through the attached worker transport', async () => {
const capabilities = [
AgentCapabilities.tools,
AgentCapabilities.execute_code,
AgentCapabilities.stateful_code_sessions,
];
const req = createMockReq(capabilities);
mockGetEndpointsConfig.mockResolvedValue(createEndpointsConfig(capabilities));
mockResolveCodeExecutionContext.mockReturnValueOnce({
baseUrl: 'http://attached-code.test/v1',
codeSessionKey: 'execute_code:stateful:attached',
executionProfile: 'stateful',
statefulSessions: true,
environmentType: 'attached',
bridgeWorkerId: 'worker-abc',
});
const toolRegistry = new Map([
[AgentConstants.BASH_TOOL, { name: AgentConstants.BASH_TOOL }],
]);
const result = await loadToolsForExecution({
req,
res: {},
agent: {
id: 'attached-agent',
tools: [Tools.execute_code],
stateful_code_sessions: true,
stateful_code_environment: 'agent-user',
},
toolNames: [AgentConstants.BASH_TOOL],
toolRegistry,
actionsEnabled: false,
});
expect(mockCreateAttachedWorkspaceBashTool).toHaveBeenCalledWith({
authHeaders: expect.any(Function),
baseUrl: 'http://attached-code.test/v1',
});
expect(result.loadedTools).toContainEqual({ name: AgentConstants.BASH_TOOL });
});
it('resolves stateful routing when handle_skill is the only requested tool', async () => {
const capabilities = [
AgentCapabilities.tools,