@librechat/agents 3.1.80-dev.2 → 3.1.80-dev.3
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.
- package/dist/cjs/main.cjs +1 -2
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +20 -78
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +5 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +26 -106
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +12 -31
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/tools/BashExecutor.mjs +20 -78
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +6 -2
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +26 -105
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +12 -31
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/types/tools/CodeExecutor.d.ts +1 -7
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +5 -0
- package/package.json +1 -1
- package/src/tools/BashExecutor.ts +24 -104
- package/src/tools/BashProgrammaticToolCalling.ts +7 -2
- package/src/tools/CodeExecutor.ts +30 -133
- package/src/tools/ProgrammaticToolCalling.ts +14 -49
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +32 -131
|
@@ -3,24 +3,11 @@ import fetch, { RequestInit } from 'node-fetch';
|
|
|
3
3
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
4
4
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
5
5
|
import type * as t from '@/types';
|
|
6
|
-
import {
|
|
6
|
+
import { emptyOutputMessage, getCodeBaseURL } from './CodeExecutor';
|
|
7
7
|
import { Constants } from '@/common';
|
|
8
8
|
|
|
9
9
|
config();
|
|
10
10
|
|
|
11
|
-
const otherMessage = 'File is already downloaded by the user';
|
|
12
|
-
const inheritedFileMessage =
|
|
13
|
-
'Available as an input — already known to 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
|
-
const inheritedFilesHeader =
|
|
19
|
-
'Available files (inputs, not generated by this execution):';
|
|
20
|
-
const generatedFilesHeader = 'Generated files:';
|
|
21
|
-
const inheritedNote =
|
|
22
|
-
'Note: Files in "Available files" are inputs the user (or a skill) already provided to the sandbox. They were not produced by this execution and you should not present them as new outputs in your response.';
|
|
23
|
-
|
|
24
11
|
const baseEndpoint = getCodeBaseURL();
|
|
25
12
|
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
26
13
|
|
|
@@ -133,54 +120,20 @@ function createBashExecutionTool(
|
|
|
133
120
|
...params,
|
|
134
121
|
};
|
|
135
122
|
|
|
123
|
+
/* See `CodeExecutor.ts` for the rationale — `/files/<session_id>`
|
|
124
|
+
* HTTP fallback was removed because codeapi's sessionAuth requires
|
|
125
|
+
* kind/id query params unavailable at this point. */
|
|
136
126
|
if (_injected_files && _injected_files.length > 0) {
|
|
137
127
|
postData.files = _injected_files;
|
|
138
|
-
} else if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (process.env.PROXY != null && process.env.PROXY !== '') {
|
|
149
|
-
fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const response = await fetch(filesEndpoint, fetchOptions);
|
|
153
|
-
if (!response.ok) {
|
|
154
|
-
throw new Error(
|
|
155
|
-
`Failed to fetch files for session: ${response.status}`
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const files = await response.json();
|
|
160
|
-
if (Array.isArray(files) && files.length > 0) {
|
|
161
|
-
const fileReferences: t.CodeEnvFile[] = files.map((file) => {
|
|
162
|
-
const nameParts = file.name.split('/');
|
|
163
|
-
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
storage_session_id: session_id,
|
|
167
|
-
/* `/files` fallback returns code-output files belonging
|
|
168
|
-
* to the user; tag them user-private. */
|
|
169
|
-
kind: 'user' as const,
|
|
170
|
-
id,
|
|
171
|
-
/* `resource_id` informational for `kind: 'user'` —
|
|
172
|
-
* codeapi derives sessionKey from auth context. */
|
|
173
|
-
resource_id: id,
|
|
174
|
-
name: file.metadata['original-filename'],
|
|
175
|
-
};
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
postData.files = fileReferences;
|
|
179
|
-
}
|
|
180
|
-
} catch {
|
|
181
|
-
// eslint-disable-next-line no-console
|
|
182
|
-
console.warn(`Failed to fetch files for session: ${session_id}`);
|
|
183
|
-
}
|
|
128
|
+
} else if (
|
|
129
|
+
session_id != null &&
|
|
130
|
+
session_id.length > 0 &&
|
|
131
|
+
!Array.isArray(postData.files)
|
|
132
|
+
) {
|
|
133
|
+
// eslint-disable-next-line no-console
|
|
134
|
+
console.debug(
|
|
135
|
+
`[BashExecutor] No injected files for session_id=${session_id} — exec will run without input files`
|
|
136
|
+
);
|
|
184
137
|
}
|
|
185
138
|
|
|
186
139
|
try {
|
|
@@ -202,6 +155,11 @@ function createBashExecutionTool(
|
|
|
202
155
|
}
|
|
203
156
|
|
|
204
157
|
const result: t.ExecuteResult = await response.json();
|
|
158
|
+
/* See `CodeExecutor.ts` — file listings were removed from the
|
|
159
|
+
* LLM-facing tool result. Bash especially benefits: models
|
|
160
|
+
* naturally `ls /mnt/data/` to discover what's available
|
|
161
|
+
* rather than relying on a prescriptive summary that
|
|
162
|
+
* misleads as often as it helps. */
|
|
205
163
|
let formattedOutput = '';
|
|
206
164
|
if (result.stdout) {
|
|
207
165
|
formattedOutput += `stdout:\n${result.stdout}\n`;
|
|
@@ -209,53 +167,15 @@ function createBashExecutionTool(
|
|
|
209
167
|
formattedOutput += emptyOutputMessage;
|
|
210
168
|
}
|
|
211
169
|
if (result.stderr) formattedOutput += `stderr:\n${result.stderr}\n`;
|
|
212
|
-
if (result.files && result.files.length > 0) {
|
|
213
|
-
/* Split inherited (read-only / unchanged-input passthroughs from
|
|
214
|
-
* codeapi) from genuine generated outputs. The LLM was previously
|
|
215
|
-
* shown skill files under "Generated files:" with the message
|
|
216
|
-
* "File is already downloaded by the user", which led it to
|
|
217
|
-
* (a) believe it had just produced files it merely referenced
|
|
218
|
-
* and (b) sometimes invent paths like /mnt/user-data/uploads/
|
|
219
|
-
* trying to find the "originals". Labeling them as inputs makes
|
|
220
|
-
* the mental model accurate. */
|
|
221
|
-
const inheritedFiles = result.files.filter(
|
|
222
|
-
(f) => f.inherited === true
|
|
223
|
-
);
|
|
224
|
-
const generatedFiles = result.files.filter(
|
|
225
|
-
(f) => f.inherited !== true
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
formattedOutput += renderFileSection(
|
|
229
|
-
generatedFilesHeader,
|
|
230
|
-
generatedFiles,
|
|
231
|
-
otherMessage
|
|
232
|
-
);
|
|
233
|
-
formattedOutput += renderFileSection(
|
|
234
|
-
inheritedFilesHeader,
|
|
235
|
-
inheritedFiles,
|
|
236
|
-
inheritedFileMessage
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
if (generatedFiles.length > 0) {
|
|
240
|
-
formattedOutput += `\n\n${accessMessage}`;
|
|
241
|
-
}
|
|
242
|
-
if (inheritedFiles.length > 0) {
|
|
243
|
-
formattedOutput += `\n\n${inheritedNote}`;
|
|
244
|
-
}
|
|
245
|
-
return [
|
|
246
|
-
formattedOutput.trim(),
|
|
247
|
-
{
|
|
248
|
-
session_id: result.session_id,
|
|
249
|
-
files: result.files,
|
|
250
|
-
} satisfies t.CodeExecutionArtifact,
|
|
251
|
-
];
|
|
252
|
-
}
|
|
253
170
|
|
|
171
|
+
const hasFiles = result.files != null && result.files.length > 0;
|
|
254
172
|
return [
|
|
255
173
|
formattedOutput.trim(),
|
|
256
|
-
|
|
257
|
-
session_id: result.session_id,
|
|
258
|
-
|
|
174
|
+
(hasFiles
|
|
175
|
+
? { session_id: result.session_id, files: result.files }
|
|
176
|
+
: {
|
|
177
|
+
session_id: result.session_id,
|
|
178
|
+
}) satisfies t.CodeExecutionArtifact,
|
|
259
179
|
];
|
|
260
180
|
} catch (error) {
|
|
261
181
|
throw new Error(
|
|
@@ -5,7 +5,6 @@ import type * as t from '@/types';
|
|
|
5
5
|
import {
|
|
6
6
|
makeRequest,
|
|
7
7
|
executeTools,
|
|
8
|
-
fetchSessionFiles,
|
|
9
8
|
formatCompletedResponse,
|
|
10
9
|
} from './ProgrammaticToolCalling';
|
|
11
10
|
import { getCodeBaseURL } from './CodeExecutor';
|
|
@@ -290,11 +289,17 @@ export function createBashProgrammaticToolCallingTool(
|
|
|
290
289
|
);
|
|
291
290
|
}
|
|
292
291
|
|
|
292
|
+
/* `/files/<session_id>` HTTP fallback removed — codeapi's
|
|
293
|
+
* sessionAuth requires kind/id query params unavailable at
|
|
294
|
+
* this point. See `CodeExecutor.ts` for full rationale. */
|
|
293
295
|
let files: t.CodeEnvFile[] | undefined;
|
|
294
296
|
if (_injected_files && _injected_files.length > 0) {
|
|
295
297
|
files = _injected_files;
|
|
296
298
|
} else if (session_id != null && session_id.length > 0) {
|
|
297
|
-
|
|
299
|
+
// eslint-disable-next-line no-console
|
|
300
|
+
console.debug(
|
|
301
|
+
`[BashProgrammaticToolCalling] No injected files for session_id=${session_id} — exec will run without input files`
|
|
302
|
+
);
|
|
298
303
|
}
|
|
299
304
|
|
|
300
305
|
let response = await makeRequest(
|
|
@@ -8,48 +8,12 @@ import { EnvVar, Constants } from '@/common';
|
|
|
8
8
|
|
|
9
9
|
config();
|
|
10
10
|
|
|
11
|
-
export const imageExtRegex = /\.(jpg|jpeg|png|gif|webp)$/i;
|
|
12
11
|
export const getCodeBaseURL = (): string =>
|
|
13
12
|
getEnvironmentVariable(EnvVar.CODE_BASEURL) ??
|
|
14
13
|
Constants.OFFICIAL_CODE_BASEURL;
|
|
15
14
|
|
|
16
|
-
const
|
|
17
|
-
const otherMessage = 'File is already downloaded by the user';
|
|
18
|
-
const inheritedFileMessage =
|
|
19
|
-
'Available as an input — already known to the user';
|
|
20
|
-
const accessMessage =
|
|
21
|
-
'Note: Files from previous executions are automatically available and can be modified.';
|
|
22
|
-
const emptyOutputMessage =
|
|
15
|
+
export const emptyOutputMessage =
|
|
23
16
|
'stdout: Empty. Ensure you\'re writing output explicitly.\n';
|
|
24
|
-
const inheritedFilesHeader =
|
|
25
|
-
'Available files (inputs, not generated by this execution):';
|
|
26
|
-
const generatedFilesHeader = 'Generated files:';
|
|
27
|
-
const inheritedNote =
|
|
28
|
-
'Note: Files in "Available files" are inputs the user (or a skill) already provided to the sandbox. They were not produced by this execution and you should not present them as new outputs in your response.';
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Renders one section of the post-execution file listing. Used by the
|
|
32
|
-
* code/bash tool formatters to keep generated outputs and inherited
|
|
33
|
-
* inputs visually separated. See BashExecutor for full docs.
|
|
34
|
-
*/
|
|
35
|
-
export function renderFileSection(
|
|
36
|
-
header: string,
|
|
37
|
-
files: t.FileRefs,
|
|
38
|
-
defaultMessage: string
|
|
39
|
-
): string {
|
|
40
|
-
if (files.length === 0) return '';
|
|
41
|
-
let out = `${header}\n`;
|
|
42
|
-
for (let i = 0; i < files.length; i++) {
|
|
43
|
-
const file = files[i];
|
|
44
|
-
const isImage = imageExtRegex.test(file.name);
|
|
45
|
-
out += `- /mnt/data/${file.name} | ${isImage ? imageMessage : defaultMessage}`;
|
|
46
|
-
if (i < files.length - 1) {
|
|
47
|
-
out += files.length <= 3 ? ', ' : ',\n';
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
out += '\n';
|
|
51
|
-
return out;
|
|
52
|
-
}
|
|
53
17
|
|
|
54
18
|
const SUPPORTED_LANGUAGES = [
|
|
55
19
|
'py',
|
|
@@ -150,65 +114,24 @@ function createCodeExecutionTool(
|
|
|
150
114
|
...params,
|
|
151
115
|
};
|
|
152
116
|
|
|
153
|
-
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*/
|
|
117
|
+
/* File injection: `_injected_files` from ToolNode (set when host
|
|
118
|
+
* primes a CodeSessionContext) or `params.files` from tool
|
|
119
|
+
* factory (set by hosts that pre-resolve at construction time).
|
|
120
|
+
* The legacy `/files/<session_id>` HTTP fallback was removed —
|
|
121
|
+
* codeapi's `sessionAuth` middleware now requires kind/id query
|
|
122
|
+
* params the tool can't supply at this point, so the fetch 400'd
|
|
123
|
+
* silently and the catch swallowed the failure. */
|
|
161
124
|
if (_injected_files && _injected_files.length > 0) {
|
|
162
125
|
postData.files = _injected_files;
|
|
163
|
-
} else if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
if (process.env.PROXY != null && process.env.PROXY !== '') {
|
|
175
|
-
fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const response = await fetch(filesEndpoint, fetchOptions);
|
|
179
|
-
if (!response.ok) {
|
|
180
|
-
throw new Error(
|
|
181
|
-
`Failed to fetch files for session: ${response.status}`
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const files = await response.json();
|
|
186
|
-
if (Array.isArray(files) && files.length > 0) {
|
|
187
|
-
const fileReferences: t.CodeEnvFile[] = files.map((file) => {
|
|
188
|
-
const nameParts = file.name.split('/');
|
|
189
|
-
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
storage_session_id: session_id,
|
|
193
|
-
/* `/files` fallback returns code-output files belonging
|
|
194
|
-
* to the user; tag them user-private. */
|
|
195
|
-
kind: 'user' as const,
|
|
196
|
-
id,
|
|
197
|
-
/* `resource_id` is informational for `kind: 'user'`
|
|
198
|
-
* (codeapi derives sessionKey from auth context); use
|
|
199
|
-
* the same value as `id` since the `/files` fallback
|
|
200
|
-
* doesn't carry separate provenance. */
|
|
201
|
-
resource_id: id,
|
|
202
|
-
name: file.metadata['original-filename'],
|
|
203
|
-
};
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
postData.files = fileReferences;
|
|
207
|
-
}
|
|
208
|
-
} catch {
|
|
209
|
-
// eslint-disable-next-line no-console
|
|
210
|
-
console.warn(`Failed to fetch files for session: ${session_id}`);
|
|
211
|
-
}
|
|
126
|
+
} else if (
|
|
127
|
+
session_id != null &&
|
|
128
|
+
session_id.length > 0 &&
|
|
129
|
+
!Array.isArray(postData.files)
|
|
130
|
+
) {
|
|
131
|
+
// eslint-disable-next-line no-console
|
|
132
|
+
console.debug(
|
|
133
|
+
`[CodeExecutor] No injected files for session_id=${session_id} — exec will run without input files`
|
|
134
|
+
);
|
|
212
135
|
}
|
|
213
136
|
|
|
214
137
|
try {
|
|
@@ -230,6 +153,13 @@ function createCodeExecutionTool(
|
|
|
230
153
|
}
|
|
231
154
|
|
|
232
155
|
const result: t.ExecuteResult = await response.json();
|
|
156
|
+
/* Output is stdout/stderr only — file listings were removed
|
|
157
|
+
* because the LLM-facing summary (split inherited/generated
|
|
158
|
+
* with prescriptive notes) caused more confusion than help,
|
|
159
|
+
* especially for bash where models naturally explore
|
|
160
|
+
* `/mnt/data/` themselves. The artifact still carries every
|
|
161
|
+
* file so the host's session map stays in sync; the LLM
|
|
162
|
+
* doesn't see them in the tool result text. */
|
|
233
163
|
let formattedOutput = '';
|
|
234
164
|
if (result.stdout) {
|
|
235
165
|
formattedOutput += `stdout:\n${result.stdout}\n`;
|
|
@@ -237,48 +167,15 @@ function createCodeExecutionTool(
|
|
|
237
167
|
formattedOutput += emptyOutputMessage;
|
|
238
168
|
}
|
|
239
169
|
if (result.stderr) formattedOutput += `stderr:\n${result.stderr}\n`;
|
|
240
|
-
if (result.files && result.files.length > 0) {
|
|
241
|
-
/* See BashExecutor for the rationale: split inherited (read-only
|
|
242
|
-
* passthrough) inputs from real generated outputs so the LLM
|
|
243
|
-
* doesn't conflate skill files with newly-produced artifacts. */
|
|
244
|
-
const inheritedFiles = result.files.filter(
|
|
245
|
-
(f) => f.inherited === true
|
|
246
|
-
);
|
|
247
|
-
const generatedFiles = result.files.filter(
|
|
248
|
-
(f) => f.inherited !== true
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
formattedOutput += renderFileSection(
|
|
252
|
-
generatedFilesHeader,
|
|
253
|
-
generatedFiles,
|
|
254
|
-
otherMessage
|
|
255
|
-
);
|
|
256
|
-
formattedOutput += renderFileSection(
|
|
257
|
-
inheritedFilesHeader,
|
|
258
|
-
inheritedFiles,
|
|
259
|
-
inheritedFileMessage
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
if (generatedFiles.length > 0) {
|
|
263
|
-
formattedOutput += `\n\n${accessMessage}`;
|
|
264
|
-
}
|
|
265
|
-
if (inheritedFiles.length > 0) {
|
|
266
|
-
formattedOutput += `\n\n${inheritedNote}`;
|
|
267
|
-
}
|
|
268
|
-
return [
|
|
269
|
-
formattedOutput.trim(),
|
|
270
|
-
{
|
|
271
|
-
session_id: result.session_id,
|
|
272
|
-
files: result.files,
|
|
273
|
-
} satisfies t.CodeExecutionArtifact,
|
|
274
|
-
];
|
|
275
|
-
}
|
|
276
170
|
|
|
171
|
+
const hasFiles = result.files != null && result.files.length > 0;
|
|
277
172
|
return [
|
|
278
173
|
formattedOutput.trim(),
|
|
279
|
-
|
|
280
|
-
session_id: result.session_id,
|
|
281
|
-
|
|
174
|
+
(hasFiles
|
|
175
|
+
? { session_id: result.session_id, files: result.files }
|
|
176
|
+
: {
|
|
177
|
+
session_id: result.session_id,
|
|
178
|
+
}) satisfies t.CodeExecutionArtifact,
|
|
282
179
|
];
|
|
283
180
|
} catch (error) {
|
|
284
181
|
throw new Error(
|
|
@@ -5,28 +5,11 @@ import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
|
5
5
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
6
6
|
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
7
7
|
import type * as t from '@/types';
|
|
8
|
-
import {
|
|
8
|
+
import { emptyOutputMessage, getCodeBaseURL } from './CodeExecutor';
|
|
9
9
|
import { Constants } from '@/common';
|
|
10
10
|
|
|
11
11
|
config();
|
|
12
12
|
|
|
13
|
-
// ============================================================================
|
|
14
|
-
// Constants
|
|
15
|
-
// ============================================================================
|
|
16
|
-
|
|
17
|
-
const otherMessage = 'File is already downloaded by the user';
|
|
18
|
-
const inheritedFileMessage =
|
|
19
|
-
'Available as an input — already known to the user';
|
|
20
|
-
const inheritedFilesHeader =
|
|
21
|
-
'Available files (inputs, not generated by this execution):';
|
|
22
|
-
const generatedFilesHeader = 'Generated files:';
|
|
23
|
-
const inheritedNote =
|
|
24
|
-
'Note: Files in "Available files" are inputs the user (or a skill) already provided to the sandbox. They were not produced by this execution and you should not present them as new outputs in your response.';
|
|
25
|
-
const accessMessage =
|
|
26
|
-
'Note: Files from previous executions are automatically available and can be modified.';
|
|
27
|
-
const emptyOutputMessage =
|
|
28
|
-
'stdout: Empty. Ensure you\'re writing output explicitly.\n';
|
|
29
|
-
|
|
30
13
|
/** Default max round-trips to prevent infinite loops */
|
|
31
14
|
const DEFAULT_MAX_ROUND_TRIPS = 20;
|
|
32
15
|
|
|
@@ -545,6 +528,11 @@ export async function executeTools(
|
|
|
545
528
|
|
|
546
529
|
/**
|
|
547
530
|
* Formats the completed response for the agent.
|
|
531
|
+
*
|
|
532
|
+
* Output is stdout/stderr only — see `CodeExecutor.ts`. The
|
|
533
|
+
* artifact still carries every file so the host's session map
|
|
534
|
+
* stays in sync; the LLM doesn't see them in the tool result text.
|
|
535
|
+
*
|
|
548
536
|
* @param response - The completed API response
|
|
549
537
|
* @returns Tuple of [formatted string, artifact]
|
|
550
538
|
*/
|
|
@@ -563,32 +551,6 @@ export function formatCompletedResponse(
|
|
|
563
551
|
formatted += `stderr:\n${response.stderr}\n`;
|
|
564
552
|
}
|
|
565
553
|
|
|
566
|
-
if (response.files && response.files.length > 0) {
|
|
567
|
-
/* See BashExecutor for the rationale: split inherited (read-only
|
|
568
|
-
* passthrough) inputs from real generated outputs so the LLM doesn't
|
|
569
|
-
* conflate skill files with newly-produced artifacts. */
|
|
570
|
-
const inheritedFiles = response.files.filter((f) => f.inherited === true);
|
|
571
|
-
const generatedFiles = response.files.filter((f) => f.inherited !== true);
|
|
572
|
-
|
|
573
|
-
formatted += renderFileSection(
|
|
574
|
-
generatedFilesHeader,
|
|
575
|
-
generatedFiles,
|
|
576
|
-
otherMessage
|
|
577
|
-
);
|
|
578
|
-
formatted += renderFileSection(
|
|
579
|
-
inheritedFilesHeader,
|
|
580
|
-
inheritedFiles,
|
|
581
|
-
inheritedFileMessage
|
|
582
|
-
);
|
|
583
|
-
|
|
584
|
-
if (generatedFiles.length > 0) {
|
|
585
|
-
formatted += `\n\n${accessMessage}`;
|
|
586
|
-
}
|
|
587
|
-
if (inheritedFiles.length > 0) {
|
|
588
|
-
formatted += `\n\n${inheritedNote}`;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
554
|
return [
|
|
593
555
|
formatted.trim(),
|
|
594
556
|
{
|
|
@@ -675,16 +637,19 @@ export function createProgrammaticToolCallingTool(
|
|
|
675
637
|
}
|
|
676
638
|
|
|
677
639
|
/**
|
|
678
|
-
* File injection
|
|
679
|
-
*
|
|
680
|
-
*
|
|
681
|
-
*
|
|
640
|
+
* File injection: `_injected_files` from ToolNode session
|
|
641
|
+
* context. The legacy `/files/<session_id>` HTTP fallback was
|
|
642
|
+
* removed (see `CodeExecutor.ts`) — codeapi's sessionAuth now
|
|
643
|
+
* requires kind/id query params unavailable at this point.
|
|
682
644
|
*/
|
|
683
645
|
let files: t.CodeEnvFile[] | undefined;
|
|
684
646
|
if (_injected_files && _injected_files.length > 0) {
|
|
685
647
|
files = _injected_files;
|
|
686
648
|
} else if (session_id != null && session_id.length > 0) {
|
|
687
|
-
|
|
649
|
+
// eslint-disable-next-line no-console
|
|
650
|
+
console.debug(
|
|
651
|
+
`[ProgrammaticToolCalling] No injected files for session_id=${session_id} — exec will run without input files`
|
|
652
|
+
);
|
|
688
653
|
}
|
|
689
654
|
|
|
690
655
|
let response = await makeRequest(
|