@librechat/agents 3.1.66-dev.0 → 3.1.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +24 -15
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +0 -13
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +0 -3
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/main.cjs +0 -40
  8. package/dist/cjs/main.cjs.map +1 -1
  9. package/dist/cjs/messages/format.cjs +12 -74
  10. package/dist/cjs/messages/format.cjs.map +1 -1
  11. package/dist/cjs/run.cjs +0 -111
  12. package/dist/cjs/run.cjs.map +1 -1
  13. package/dist/cjs/tools/ToolNode.cjs +140 -304
  14. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  15. package/dist/esm/agents/AgentContext.mjs +24 -15
  16. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  17. package/dist/esm/common/enum.mjs +1 -12
  18. package/dist/esm/common/enum.mjs.map +1 -1
  19. package/dist/esm/graphs/Graph.mjs +0 -3
  20. package/dist/esm/graphs/Graph.mjs.map +1 -1
  21. package/dist/esm/main.mjs +1 -10
  22. package/dist/esm/main.mjs.map +1 -1
  23. package/dist/esm/messages/format.mjs +4 -66
  24. package/dist/esm/messages/format.mjs.map +1 -1
  25. package/dist/esm/run.mjs +0 -111
  26. package/dist/esm/run.mjs.map +1 -1
  27. package/dist/esm/tools/ToolNode.mjs +142 -306
  28. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  29. package/dist/types/agents/AgentContext.d.ts +6 -0
  30. package/dist/types/common/enum.d.ts +1 -7
  31. package/dist/types/graphs/Graph.d.ts +0 -2
  32. package/dist/types/index.d.ts +0 -6
  33. package/dist/types/messages/format.d.ts +1 -2
  34. package/dist/types/run.d.ts +0 -1
  35. package/dist/types/tools/ToolNode.d.ts +2 -24
  36. package/dist/types/types/index.d.ts +0 -1
  37. package/dist/types/types/llm.d.ts +14 -2
  38. package/dist/types/types/run.d.ts +0 -20
  39. package/dist/types/types/tools.d.ts +1 -38
  40. package/package.json +1 -1
  41. package/src/agents/AgentContext.ts +28 -15
  42. package/src/agents/__tests__/AgentContext.test.ts +110 -0
  43. package/src/common/enum.ts +0 -12
  44. package/src/graphs/Graph.ts +0 -4
  45. package/src/index.ts +0 -8
  46. package/src/messages/format.ts +4 -74
  47. package/src/run.ts +0 -126
  48. package/src/tools/ToolNode.ts +169 -391
  49. package/src/tools/__tests__/ToolNode.session.test.ts +12 -12
  50. package/src/types/index.ts +0 -1
  51. package/src/types/llm.ts +16 -2
  52. package/src/types/run.ts +0 -20
  53. package/src/types/tools.ts +1 -41
  54. package/dist/cjs/hooks/HookRegistry.cjs +0 -162
  55. package/dist/cjs/hooks/HookRegistry.cjs.map +0 -1
  56. package/dist/cjs/hooks/executeHooks.cjs +0 -276
  57. package/dist/cjs/hooks/executeHooks.cjs.map +0 -1
  58. package/dist/cjs/hooks/matchers.cjs +0 -256
  59. package/dist/cjs/hooks/matchers.cjs.map +0 -1
  60. package/dist/cjs/hooks/types.cjs +0 -27
  61. package/dist/cjs/hooks/types.cjs.map +0 -1
  62. package/dist/cjs/tools/BashExecutor.cjs +0 -175
  63. package/dist/cjs/tools/BashExecutor.cjs.map +0 -1
  64. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +0 -296
  65. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +0 -1
  66. package/dist/cjs/tools/ReadFile.cjs +0 -43
  67. package/dist/cjs/tools/ReadFile.cjs.map +0 -1
  68. package/dist/cjs/tools/SkillTool.cjs +0 -50
  69. package/dist/cjs/tools/SkillTool.cjs.map +0 -1
  70. package/dist/cjs/tools/skillCatalog.cjs +0 -84
  71. package/dist/cjs/tools/skillCatalog.cjs.map +0 -1
  72. package/dist/esm/hooks/HookRegistry.mjs +0 -160
  73. package/dist/esm/hooks/HookRegistry.mjs.map +0 -1
  74. package/dist/esm/hooks/executeHooks.mjs +0 -273
  75. package/dist/esm/hooks/executeHooks.mjs.map +0 -1
  76. package/dist/esm/hooks/matchers.mjs +0 -251
  77. package/dist/esm/hooks/matchers.mjs.map +0 -1
  78. package/dist/esm/hooks/types.mjs +0 -25
  79. package/dist/esm/hooks/types.mjs.map +0 -1
  80. package/dist/esm/tools/BashExecutor.mjs +0 -169
  81. package/dist/esm/tools/BashExecutor.mjs.map +0 -1
  82. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +0 -287
  83. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +0 -1
  84. package/dist/esm/tools/ReadFile.mjs +0 -38
  85. package/dist/esm/tools/ReadFile.mjs.map +0 -1
  86. package/dist/esm/tools/SkillTool.mjs +0 -45
  87. package/dist/esm/tools/SkillTool.mjs.map +0 -1
  88. package/dist/esm/tools/skillCatalog.mjs +0 -82
  89. package/dist/esm/tools/skillCatalog.mjs.map +0 -1
  90. package/dist/types/hooks/HookRegistry.d.ts +0 -56
  91. package/dist/types/hooks/executeHooks.d.ts +0 -79
  92. package/dist/types/hooks/index.d.ts +0 -6
  93. package/dist/types/hooks/matchers.d.ts +0 -95
  94. package/dist/types/hooks/types.d.ts +0 -309
  95. package/dist/types/tools/BashExecutor.d.ts +0 -45
  96. package/dist/types/tools/BashProgrammaticToolCalling.d.ts +0 -72
  97. package/dist/types/tools/ReadFile.d.ts +0 -28
  98. package/dist/types/tools/SkillTool.d.ts +0 -40
  99. package/dist/types/tools/skillCatalog.d.ts +0 -19
  100. package/dist/types/types/skill.d.ts +0 -9
  101. package/src/hooks/HookRegistry.ts +0 -208
  102. package/src/hooks/__tests__/HookRegistry.test.ts +0 -190
  103. package/src/hooks/__tests__/executeHooks.test.ts +0 -1013
  104. package/src/hooks/__tests__/integration.test.ts +0 -337
  105. package/src/hooks/__tests__/matchers.test.ts +0 -238
  106. package/src/hooks/__tests__/toolHooks.test.ts +0 -669
  107. package/src/hooks/executeHooks.ts +0 -375
  108. package/src/hooks/index.ts +0 -55
  109. package/src/hooks/matchers.ts +0 -280
  110. package/src/hooks/types.ts +0 -388
  111. package/src/messages/formatAgentMessages.skills.test.ts +0 -334
  112. package/src/tools/BashExecutor.ts +0 -205
  113. package/src/tools/BashProgrammaticToolCalling.ts +0 -397
  114. package/src/tools/ReadFile.ts +0 -39
  115. package/src/tools/SkillTool.ts +0 -46
  116. package/src/tools/__tests__/ReadFile.test.ts +0 -44
  117. package/src/tools/__tests__/SkillTool.test.ts +0 -442
  118. package/src/tools/__tests__/skillCatalog.test.ts +0 -161
  119. package/src/tools/skillCatalog.ts +0 -126
  120. package/src/types/skill.ts +0 -11
@@ -1,334 +0,0 @@
1
- import { HumanMessage } from '@langchain/core/messages';
2
- import type { TPayload } from '@/types';
3
- import { formatAgentMessages } from './format';
4
- import { ContentTypes, Constants } from '@/common';
5
-
6
- /** Helper to build a skill tool_call content part */
7
- function skillToolCall(
8
- id: string,
9
- skillName: string,
10
- output = 'Skill loaded.',
11
- ): Record<string, unknown> {
12
- return {
13
- type: ContentTypes.TOOL_CALL,
14
- tool_call: {
15
- id,
16
- name: Constants.SKILL_TOOL,
17
- args: JSON.stringify({ skillName }),
18
- output,
19
- },
20
- };
21
- }
22
-
23
- describe('formatAgentMessages skill body reconstruction', () => {
24
- const skillBodies = new Map([
25
- ['pdf-analyzer', '# PDF Analyzer\nAnalyze PDF files step by step.'],
26
- ['code-review', '# Code Review\nReview the code for issues.'],
27
- ]);
28
-
29
- describe('with discoveredTools (tools filtering active)', () => {
30
- const tools = new Set([Constants.SKILL_TOOL, 'web_search']);
31
-
32
- it('reconstructs HumanMessage after skill ToolMessage', () => {
33
- const payload: TPayload = [
34
- { role: 'user', content: 'Analyze this PDF' },
35
- {
36
- role: 'assistant',
37
- content: [
38
- {
39
- type: ContentTypes.TEXT,
40
- [ContentTypes.TEXT]: 'I\'ll invoke the skill.',
41
- tool_call_ids: ['call_1'],
42
- },
43
- skillToolCall('call_1', 'pdf-analyzer'),
44
- ],
45
- },
46
- ];
47
-
48
- const { messages } = formatAgentMessages(payload, undefined, tools, skillBodies);
49
-
50
- // user, AI, ToolMessage, injected HumanMessage
51
- expect(messages.length).toBeGreaterThanOrEqual(4);
52
- const last = messages[messages.length - 1];
53
- expect(last).toBeInstanceOf(HumanMessage);
54
- expect(last.content).toBe('# PDF Analyzer\nAnalyze PDF files step by step.');
55
- expect((last as HumanMessage).additional_kwargs.source).toBe('skill');
56
- expect((last as HumanMessage).additional_kwargs.skillName).toBe('pdf-analyzer');
57
- expect((last as HumanMessage).additional_kwargs.isMeta).toBe(true);
58
- });
59
-
60
- it('does NOT inject body when skill tool is not in discoveredTools', () => {
61
- const restrictedTools = new Set(['web_search']); // skill NOT allowed
62
- const payload: TPayload = [
63
- { role: 'user', content: 'Analyze this' },
64
- {
65
- role: 'assistant',
66
- content: [skillToolCall('call_1', 'pdf-analyzer')],
67
- },
68
- ];
69
-
70
- const { messages } = formatAgentMessages(payload, undefined, restrictedTools, skillBodies);
71
-
72
- const humanMessages = messages.filter((m) => m instanceof HumanMessage);
73
- // Only the user message, no injected skill body
74
- expect(humanMessages).toHaveLength(1);
75
- });
76
-
77
- it('does not inject when skill name is not in skills Map', () => {
78
- const payload: TPayload = [
79
- { role: 'user', content: 'Hello' },
80
- {
81
- role: 'assistant',
82
- content: [skillToolCall('call_1', 'unknown-skill')],
83
- },
84
- ];
85
-
86
- const { messages } = formatAgentMessages(payload, undefined, tools, skillBodies);
87
-
88
- const humanMessages = messages.filter((m) => m instanceof HumanMessage);
89
- expect(humanMessages).toHaveLength(1); // only the user message
90
- });
91
- });
92
-
93
- describe('without discoveredTools (no tools filtering)', () => {
94
- it('reconstructs HumanMessage when skills Map provided', () => {
95
- const payload: TPayload = [
96
- { role: 'user', content: 'Review my code' },
97
- {
98
- role: 'assistant',
99
- content: [skillToolCall('call_1', 'code-review')],
100
- },
101
- ];
102
-
103
- const { messages } = formatAgentMessages(payload, undefined, undefined, skillBodies);
104
-
105
- const injected = messages.filter(
106
- (m) => m instanceof HumanMessage && (m as HumanMessage).additional_kwargs?.source === 'skill',
107
- );
108
- expect(injected).toHaveLength(1);
109
- expect(injected[0].content).toBe('# Code Review\nReview the code for issues.');
110
- });
111
-
112
- it('no injection when skills Map is undefined', () => {
113
- const payload: TPayload = [
114
- { role: 'user', content: 'Hello' },
115
- {
116
- role: 'assistant',
117
- content: [skillToolCall('call_1', 'pdf-analyzer')],
118
- },
119
- ];
120
-
121
- const { messages } = formatAgentMessages(payload, undefined, undefined, undefined);
122
-
123
- const injected = messages.filter(
124
- (m) => m instanceof HumanMessage && (m as HumanMessage).additional_kwargs?.source === 'skill',
125
- );
126
- expect(injected).toHaveLength(0);
127
- });
128
-
129
- it('no injection when skills Map is empty', () => {
130
- const payload: TPayload = [
131
- { role: 'user', content: 'Hello' },
132
- {
133
- role: 'assistant',
134
- content: [skillToolCall('call_1', 'pdf-analyzer')],
135
- },
136
- ];
137
-
138
- const { messages } = formatAgentMessages(payload, undefined, undefined, new Map());
139
-
140
- const injected = messages.filter(
141
- (m) => m instanceof HumanMessage && (m as HumanMessage).additional_kwargs?.source === 'skill',
142
- );
143
- expect(injected).toHaveLength(0);
144
- });
145
- });
146
-
147
- describe('extractSkillName edge cases', () => {
148
- const tools = new Set([Constants.SKILL_TOOL]);
149
-
150
- it('handles object args (not stringified)', () => {
151
- const payload: TPayload = [
152
- { role: 'user', content: 'Go' },
153
- {
154
- role: 'assistant',
155
- content: [
156
- {
157
- type: ContentTypes.TOOL_CALL,
158
- tool_call: {
159
- id: 'call_1',
160
- name: Constants.SKILL_TOOL,
161
- args: { skillName: 'pdf-analyzer' }, // object, not string
162
- output: 'Loaded.',
163
- },
164
- },
165
- ],
166
- },
167
- ];
168
-
169
- const { messages } = formatAgentMessages(payload, undefined, tools, skillBodies);
170
-
171
- const injected = messages.filter(
172
- (m) => m instanceof HumanMessage && (m as HumanMessage).additional_kwargs?.source === 'skill',
173
- );
174
- expect(injected).toHaveLength(1);
175
- });
176
-
177
- it('gracefully skips malformed JSON args', () => {
178
- const payload: TPayload = [
179
- { role: 'user', content: 'Go' },
180
- {
181
- role: 'assistant',
182
- content: [
183
- {
184
- type: ContentTypes.TOOL_CALL,
185
- tool_call: {
186
- id: 'call_1',
187
- name: Constants.SKILL_TOOL,
188
- args: '{bad json',
189
- output: 'Loaded.',
190
- },
191
- },
192
- ],
193
- },
194
- ];
195
-
196
- const { messages } = formatAgentMessages(payload, undefined, tools, skillBodies);
197
-
198
- const injected = messages.filter(
199
- (m) => m instanceof HumanMessage && (m as HumanMessage).additional_kwargs?.source === 'skill',
200
- );
201
- expect(injected).toHaveLength(0); // gracefully skipped
202
- });
203
-
204
- it('skips empty skillName', () => {
205
- const payload: TPayload = [
206
- { role: 'user', content: 'Go' },
207
- {
208
- role: 'assistant',
209
- content: [
210
- {
211
- type: ContentTypes.TOOL_CALL,
212
- tool_call: {
213
- id: 'call_1',
214
- name: Constants.SKILL_TOOL,
215
- args: JSON.stringify({ skillName: '' }),
216
- output: 'Loaded.',
217
- },
218
- },
219
- ],
220
- },
221
- ];
222
-
223
- const { messages } = formatAgentMessages(payload, undefined, tools, skillBodies);
224
-
225
- const injected = messages.filter(
226
- (m) => m instanceof HumanMessage && (m as HumanMessage).additional_kwargs?.source === 'skill',
227
- );
228
- expect(injected).toHaveLength(0);
229
- });
230
- });
231
-
232
- describe('deduplication', () => {
233
- const tools = new Set([Constants.SKILL_TOOL]);
234
-
235
- it('injects body only once when same skill invoked twice in one message', () => {
236
- const payload: TPayload = [
237
- { role: 'user', content: 'Go' },
238
- {
239
- role: 'assistant',
240
- content: [
241
- skillToolCall('call_1', 'pdf-analyzer'),
242
- skillToolCall('call_2', 'pdf-analyzer'),
243
- ],
244
- },
245
- ];
246
-
247
- const { messages } = formatAgentMessages(payload, undefined, tools, skillBodies);
248
-
249
- const injected = messages.filter(
250
- (m) => m instanceof HumanMessage && (m as HumanMessage).additional_kwargs?.source === 'skill',
251
- );
252
- expect(injected).toHaveLength(1);
253
- });
254
-
255
- it('injects body for each distinct skill invoked', () => {
256
- const payload: TPayload = [
257
- { role: 'user', content: 'Go' },
258
- {
259
- role: 'assistant',
260
- content: [
261
- skillToolCall('call_1', 'pdf-analyzer'),
262
- skillToolCall('call_2', 'code-review'),
263
- ],
264
- },
265
- ];
266
-
267
- const { messages } = formatAgentMessages(payload, undefined, tools, skillBodies);
268
-
269
- const injected = messages.filter(
270
- (m) => m instanceof HumanMessage && (m as HumanMessage).additional_kwargs?.source === 'skill',
271
- );
272
- expect(injected).toHaveLength(2);
273
- const names = injected.map((m) => (m as HumanMessage).additional_kwargs.skillName);
274
- expect(names).toContain('pdf-analyzer');
275
- expect(names).toContain('code-review');
276
- });
277
- });
278
-
279
- describe('indexTokenCountMap distribution', () => {
280
- const tools = new Set([Constants.SKILL_TOOL]);
281
-
282
- it('excludes injected HumanMessages from assistant token distribution', () => {
283
- const payload: TPayload = [
284
- { role: 'user', content: 'Analyze this' },
285
- {
286
- role: 'assistant',
287
- content: [
288
- {
289
- type: ContentTypes.TEXT,
290
- [ContentTypes.TEXT]: 'Invoking skill.',
291
- tool_call_ids: ['call_1'],
292
- },
293
- skillToolCall('call_1', 'pdf-analyzer'),
294
- ],
295
- },
296
- ];
297
-
298
- const inputTokenMap: Record<number, number | undefined> = {
299
- 0: 100, // user message
300
- 1: 500, // assistant message
301
- };
302
-
303
- const { messages, indexTokenCountMap } = formatAgentMessages(
304
- payload,
305
- inputTokenMap,
306
- tools,
307
- skillBodies,
308
- );
309
-
310
- // There should be messages: user, AI, ToolMessage, injected HumanMessage
311
- expect(messages.length).toBeGreaterThanOrEqual(4);
312
- const lastMsg = messages[messages.length - 1];
313
- expect(lastMsg).toBeInstanceOf(HumanMessage);
314
- expect((lastMsg as HumanMessage).additional_kwargs.source).toBe('skill');
315
-
316
- // Token map must be defined when input was provided
317
- expect(indexTokenCountMap).toBeDefined();
318
-
319
- // The injected HumanMessage's index should NOT be in the token map
320
- const injectedIndex = messages.length - 1;
321
- expect(indexTokenCountMap![injectedIndex]).toBeUndefined();
322
-
323
- // The assistant's 500 tokens should be distributed only across
324
- // the AI + ToolMessage, NOT the injected HumanMessage
325
- let assistantTotal = 0;
326
- for (const [idx, count] of Object.entries(indexTokenCountMap!)) {
327
- if (Number(idx) > 0 && Number(idx) < injectedIndex) {
328
- assistantTotal += count ?? 0;
329
- }
330
- }
331
- expect(assistantTotal).toBe(500);
332
- });
333
- });
334
- });
@@ -1,205 +0,0 @@
1
- import { config } from 'dotenv';
2
- import fetch, { RequestInit } from 'node-fetch';
3
- import { HttpsProxyAgent } from 'https-proxy-agent';
4
- import { tool, DynamicStructuredTool } from '@langchain/core/tools';
5
- import { getEnvironmentVariable } from '@langchain/core/utils/env';
6
- import type * as t from '@/types';
7
- import { imageExtRegex, getCodeBaseURL } from './CodeExecutor';
8
- import { EnvVar, Constants } from '@/common';
9
-
10
- config();
11
-
12
- const imageMessage = 'Image is already displayed to the user';
13
- const otherMessage = 'File is already downloaded by the user';
14
- const accessMessage =
15
- 'Note: Files from previous executions are automatically available and can be modified.';
16
- const emptyOutputMessage =
17
- 'stdout: Empty. Ensure you\'re writing output explicitly.\n';
18
-
19
- const baseEndpoint = getCodeBaseURL();
20
- const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
21
-
22
- export const BashExecutionToolSchema = {
23
- type: 'object',
24
- properties: {
25
- command: {
26
- type: 'string',
27
- description: `The bash command or script to execute.
28
- - The environment is stateless; variables and state don't persist between executions.
29
- - Generated files from previous executions are automatically available in "/mnt/data/".
30
- - Files from previous executions are automatically available and can be modified in place.
31
- - Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.
32
- - Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.
33
- - IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.
34
- - Use \`echo\`, \`printf\`, or \`cat\` for all outputs.`,
35
- },
36
- args: {
37
- type: 'array',
38
- items: { type: 'string' },
39
- description:
40
- 'Additional arguments to execute the command with. This should only be used if the input command requires additional arguments to run.',
41
- },
42
- },
43
- required: ['command'],
44
- } as const;
45
-
46
- export const BashExecutionToolDescription = `
47
- Runs bash commands and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.
48
-
49
- Usage:
50
- - No network access available.
51
- - Generated files are automatically delivered; **DO NOT** provide download links.
52
- - NEVER use this tool to execute malicious commands.
53
- `.trim();
54
-
55
- export const BashExecutionToolName = Constants.BASH_TOOL;
56
-
57
- export const BashExecutionToolDefinition = {
58
- name: BashExecutionToolName,
59
- description: BashExecutionToolDescription,
60
- schema: BashExecutionToolSchema,
61
- } as const;
62
-
63
- function createBashExecutionTool(
64
- params: t.BashExecutionToolParams = {}
65
- ): DynamicStructuredTool {
66
- const apiKey =
67
- params[EnvVar.CODE_API_KEY] ??
68
- params.apiKey ??
69
- getEnvironmentVariable(EnvVar.CODE_API_KEY) ??
70
- '';
71
- if (!apiKey) {
72
- throw new Error('No API key provided for bash execution tool.');
73
- }
74
-
75
- return tool(
76
- async (rawInput, config) => {
77
- const { command, ...rest } = rawInput as {
78
- command: string;
79
- args?: string[];
80
- };
81
- const { session_id, _injected_files } = (config.toolCall ?? {}) as {
82
- session_id?: string;
83
- _injected_files?: t.CodeEnvFile[];
84
- };
85
-
86
- const postData: Record<string, unknown> = {
87
- lang: 'bash',
88
- code: command,
89
- ...rest,
90
- ...params,
91
- };
92
-
93
- if (_injected_files && _injected_files.length > 0) {
94
- postData.files = _injected_files;
95
- } else if (session_id != null && session_id.length > 0) {
96
- try {
97
- const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;
98
- const fetchOptions: RequestInit = {
99
- method: 'GET',
100
- headers: {
101
- 'User-Agent': 'LibreChat/1.0',
102
- 'X-API-Key': apiKey,
103
- },
104
- };
105
-
106
- if (process.env.PROXY != null && process.env.PROXY !== '') {
107
- fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);
108
- }
109
-
110
- const response = await fetch(filesEndpoint, fetchOptions);
111
- if (!response.ok) {
112
- throw new Error(
113
- `Failed to fetch files for session: ${response.status}`
114
- );
115
- }
116
-
117
- const files = await response.json();
118
- if (Array.isArray(files) && files.length > 0) {
119
- const fileReferences: t.CodeEnvFile[] = files.map((file) => {
120
- const nameParts = file.name.split('/');
121
- const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
122
-
123
- return {
124
- session_id,
125
- id,
126
- name: file.metadata['original-filename'],
127
- };
128
- });
129
-
130
- postData.files = fileReferences;
131
- }
132
- } catch {
133
- // eslint-disable-next-line no-console
134
- console.warn(`Failed to fetch files for session: ${session_id}`);
135
- }
136
- }
137
-
138
- try {
139
- const fetchOptions: RequestInit = {
140
- method: 'POST',
141
- headers: {
142
- 'Content-Type': 'application/json',
143
- 'User-Agent': 'LibreChat/1.0',
144
- 'X-API-Key': apiKey,
145
- },
146
- body: JSON.stringify(postData),
147
- };
148
-
149
- if (process.env.PROXY != null && process.env.PROXY !== '') {
150
- fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);
151
- }
152
- const response = await fetch(EXEC_ENDPOINT, fetchOptions);
153
- if (!response.ok) {
154
- throw new Error(`HTTP error! status: ${response.status}`);
155
- }
156
-
157
- const result: t.ExecuteResult = await response.json();
158
- let formattedOutput = '';
159
- if (result.stdout) {
160
- formattedOutput += `stdout:\n${result.stdout}\n`;
161
- } else {
162
- formattedOutput += emptyOutputMessage;
163
- }
164
- if (result.stderr) formattedOutput += `stderr:\n${result.stderr}\n`;
165
- if (result.files && result.files.length > 0) {
166
- formattedOutput += 'Generated files:\n';
167
-
168
- const fileCount = result.files.length;
169
- for (let i = 0; i < fileCount; i++) {
170
- const file = result.files[i];
171
- const isImage = imageExtRegex.test(file.name);
172
- formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;
173
-
174
- if (i < fileCount - 1) {
175
- formattedOutput += fileCount <= 3 ? ', ' : ',\n';
176
- }
177
- }
178
-
179
- formattedOutput += `\n\n${accessMessage}`;
180
- return [
181
- formattedOutput.trim(),
182
- {
183
- session_id: result.session_id,
184
- files: result.files,
185
- },
186
- ];
187
- }
188
-
189
- return [formattedOutput.trim(), { session_id: result.session_id }];
190
- } catch (error) {
191
- throw new Error(
192
- `Execution error:\n\n${(error as Error | undefined)?.message}`
193
- );
194
- }
195
- },
196
- {
197
- name: BashExecutionToolName,
198
- description: BashExecutionToolDescription,
199
- schema: BashExecutionToolSchema,
200
- responseFormat: Constants.CONTENT_AND_ARTIFACT,
201
- }
202
- );
203
- }
204
-
205
- export { createBashExecutionTool };