import { Constants } from '@librechat/agents'; import type { ToolExecuteBatchRequest, ToolExecuteResult, ToolCallRequest, } from '@librechat/agents'; import { createToolExecuteHandler, ToolExecuteOptions } from './handlers'; function createMockTool(name: string, capturedConfigs: Record[]) { return { name, invoke: jest.fn(async (_args: unknown, config: Record) => { capturedConfigs.push({ ...(config.toolCall as Record) }); return { content: `stdout:\n${name} executed\n`, artifact: { session_id: `result-session-${name}`, files: [] }, }; }), }; } function createHandler( capturedConfigs: Record[], toolNames: string[] = [Constants.EXECUTE_CODE], ) { const mockTools = toolNames.map((name) => createMockTool(name, capturedConfigs)); const loadTools: ToolExecuteOptions['loadTools'] = jest.fn(async () => ({ loadedTools: mockTools as never[], })); return createToolExecuteHandler({ loadTools }); } function invokeHandler( handler: ReturnType, toolCalls: ToolCallRequest[], ): Promise { return new Promise((resolve, reject) => { const request: ToolExecuteBatchRequest = { toolCalls, resolve, reject, }; handler.handle('on_tool_execute', request); }); } describe('createToolExecuteHandler', () => { describe('code execution session context passthrough', () => { it('passes session_id and _injected_files from codeSessionContext to toolCallConfig', async () => { const capturedConfigs: Record[] = []; const handler = createHandler(capturedConfigs); const toolCalls: ToolCallRequest[] = [ { id: 'call_1', name: Constants.EXECUTE_CODE, args: { lang: 'python', code: 'print("hi")' }, codeSessionContext: { session_id: 'prev-session-abc', files: [ { session_id: 'prev-session-abc', id: 'f1', name: 'data.parquet' }, { session_id: 'prev-session-abc', id: 'f2', name: 'chart.png' }, ], }, }, ]; await invokeHandler(handler, toolCalls); expect(capturedConfigs).toHaveLength(1); expect(capturedConfigs[0].session_id).toBe('prev-session-abc'); expect(capturedConfigs[0]._injected_files).toEqual([ { session_id: 'prev-session-abc', id: 'f1', name: 'data.parquet' }, { session_id: 'prev-session-abc', id: 'f2', name: 'chart.png' }, ]); }); it('passes session_id without _injected_files when session has no files', async () => { const capturedConfigs: Record[] = []; const handler = createHandler(capturedConfigs); const toolCalls: ToolCallRequest[] = [ { id: 'call_2', name: Constants.EXECUTE_CODE, args: { lang: 'python', code: 'import pandas' }, codeSessionContext: { session_id: 'session-no-files', }, }, ]; await invokeHandler(handler, toolCalls); expect(capturedConfigs).toHaveLength(1); expect(capturedConfigs[0].session_id).toBe('session-no-files'); expect(capturedConfigs[0]._injected_files).toBeUndefined(); }); it('does not inject session context when codeSessionContext is absent', async () => { const capturedConfigs: Record[] = []; const handler = createHandler(capturedConfigs); const toolCalls: ToolCallRequest[] = [ { id: 'call_3', name: Constants.EXECUTE_CODE, args: { lang: 'python', code: 'x = 1' }, }, ]; await invokeHandler(handler, toolCalls); expect(capturedConfigs).toHaveLength(1); expect(capturedConfigs[0].session_id).toBeUndefined(); expect(capturedConfigs[0]._injected_files).toBeUndefined(); }); it('passes session context independently for multiple code execution calls', async () => { const capturedConfigs: Record[] = []; const handler = createHandler(capturedConfigs); const toolCalls: ToolCallRequest[] = [ { id: 'call_a', name: Constants.EXECUTE_CODE, args: { lang: 'python', code: 'step_1()' }, codeSessionContext: { session_id: 'session-A', files: [{ session_id: 'session-A', id: 'fa', name: 'a.csv' }], }, }, { id: 'call_b', name: Constants.EXECUTE_CODE, args: { lang: 'python', code: 'step_2()' }, codeSessionContext: { session_id: 'session-A', files: [{ session_id: 'session-A', id: 'fa', name: 'a.csv' }], }, }, ]; await invokeHandler(handler, toolCalls); expect(capturedConfigs).toHaveLength(2); for (const config of capturedConfigs) { expect(config.session_id).toBe('session-A'); expect(config._injected_files).toEqual([ { session_id: 'session-A', id: 'fa', name: 'a.csv' }, ]); } }); it('does not pass session context to non-code-execution tools', async () => { const capturedConfigs: Record[] = []; const handler = createHandler(capturedConfigs, ['web_search']); const toolCalls: ToolCallRequest[] = [ { id: 'call_ws', name: 'web_search', args: { query: 'test' }, codeSessionContext: { session_id: 'should-be-ignored', files: [{ session_id: 'x', id: 'y', name: 'z' }], }, }, ]; await invokeHandler(handler, toolCalls); expect(capturedConfigs).toHaveLength(1); expect(capturedConfigs[0].session_id).toBeUndefined(); expect(capturedConfigs[0]._injected_files).toBeUndefined(); }); }); });