@librechat/agents 3.1.89 → 3.1.90
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/graphs/Graph.cjs +7 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/executeHooks.cjs +14 -7
- package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +8 -2
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +34 -0
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/main.cjs +9 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +10 -9
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +12 -8
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +35 -11
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeSessionFileSummary.cjs +63 -0
- package/dist/cjs/tools/CodeSessionFileSummary.cjs.map +1 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +16 -12
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +8 -5
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +319 -29
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +7 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/executeHooks.mjs +14 -7
- package/dist/esm/hooks/executeHooks.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +9 -3
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +33 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +11 -10
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +13 -9
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +29 -12
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeSessionFileSummary.mjs +60 -0
- package/dist/esm/tools/CodeSessionFileSummary.mjs.map +1 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +17 -13
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +8 -5
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +320 -31
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/types/llm/anthropic/index.d.ts +3 -1
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +4 -0
- package/dist/types/tools/BashExecutor.d.ts +3 -3
- package/dist/types/tools/CodeExecutor.d.ts +10 -3
- package/dist/types/tools/CodeSessionFileSummary.d.ts +3 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +4 -4
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +8 -5
- package/dist/types/types/tools.d.ts +2 -3
- package/package.json +1 -1
- package/src/graphs/Graph.ts +7 -0
- package/src/hooks/__tests__/executeHooks.test.ts +38 -0
- package/src/hooks/executeHooks.ts +27 -7
- package/src/llm/anthropic/index.ts +27 -3
- package/src/llm/anthropic/llm.spec.ts +60 -1
- package/src/llm/anthropic/utils/message_inputs.ts +46 -0
- package/src/tools/BashExecutor.ts +21 -10
- package/src/tools/BashProgrammaticToolCalling.ts +21 -9
- package/src/tools/CodeExecutor.ts +55 -12
- package/src/tools/CodeSessionFileSummary.ts +80 -0
- package/src/tools/ProgrammaticToolCalling.ts +25 -12
- package/src/tools/ToolNode.ts +8 -5
- package/src/tools/__tests__/BashExecutor.test.ts +9 -0
- package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +43 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +100 -16
- package/src/tools/__tests__/SubagentExecutor.test.ts +540 -6
- package/src/tools/__tests__/ToolNode.outputReferences.test.ts +52 -0
- package/src/tools/__tests__/subagentHooks.test.ts +237 -0
- package/src/tools/subagent/SubagentExecutor.ts +514 -36
- package/src/types/tools.ts +2 -3
package/src/tools/ToolNode.ts
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
buildReferenceKey,
|
|
47
47
|
ToolOutputReferenceRegistry,
|
|
48
48
|
} from '@/tools/toolOutputReferences';
|
|
49
|
+
import { stripCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';
|
|
49
50
|
import {
|
|
50
51
|
resolveLocalToolRegistry,
|
|
51
52
|
resolveLocalExecutionTools,
|
|
@@ -911,8 +912,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
911
912
|
* Both session_id and _injected_files are injected directly to invokeParams
|
|
912
913
|
* (not inside args) so they bypass Zod schema validation and reach config.toolCall.
|
|
913
914
|
*
|
|
914
|
-
* session_id is always injected when available
|
|
915
|
-
*
|
|
915
|
+
* session_id is always injected when available, but concrete file refs
|
|
916
|
+
* still need to travel through `_injected_files`; the legacy
|
|
917
|
+
* `/files/<session_id>` fallback was removed from the executors.
|
|
916
918
|
*/
|
|
917
919
|
if (CODE_EXECUTION_TOOLS.has(call.name)) {
|
|
918
920
|
const codeSession = this.sessions?.get(Constants.EXECUTE_CODE) as
|
|
@@ -959,6 +961,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
959
961
|
if (this.toolOutputRegistry != null || unresolvedRefs.length > 0) {
|
|
960
962
|
if (typeof toolMsg.content === 'string') {
|
|
961
963
|
const rawContent = toolMsg.content;
|
|
964
|
+
const registryContent = stripCodeSessionFileSummary(rawContent);
|
|
962
965
|
const llmContent = truncateToolResultContent(
|
|
963
966
|
rawContent,
|
|
964
967
|
this.maxToolResultChars
|
|
@@ -966,7 +969,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
966
969
|
toolMsg.content = llmContent;
|
|
967
970
|
const refMeta = this.recordOutputReference(
|
|
968
971
|
runId,
|
|
969
|
-
|
|
972
|
+
registryContent,
|
|
970
973
|
refKey,
|
|
971
974
|
unresolvedRefs
|
|
972
975
|
);
|
|
@@ -1015,7 +1018,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1015
1018
|
);
|
|
1016
1019
|
const refMeta = this.recordOutputReference(
|
|
1017
1020
|
runId,
|
|
1018
|
-
rawContent,
|
|
1021
|
+
stripCodeSessionFileSummary(rawContent),
|
|
1019
1022
|
refKey,
|
|
1020
1023
|
unresolvedRefs
|
|
1021
1024
|
);
|
|
@@ -2661,7 +2664,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2661
2664
|
: undefined;
|
|
2662
2665
|
const successRefMeta = this.recordOutputReference(
|
|
2663
2666
|
registryRunId,
|
|
2664
|
-
registryRaw,
|
|
2667
|
+
stripCodeSessionFileSummary(registryRaw),
|
|
2665
2668
|
refKey,
|
|
2666
2669
|
unresolved
|
|
2667
2670
|
);
|
|
@@ -18,6 +18,15 @@ describe('buildBashExecutionToolDescription', () => {
|
|
|
18
18
|
).toBe(BashExecutionToolDescription);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
+
it('warns about compact bash shell pitfalls', () => {
|
|
22
|
+
expect(BashExecutionToolDescription).toContain('heredoc/printf');
|
|
23
|
+
expect(BashExecutionToolDescription).toContain('not bare Python');
|
|
24
|
+
expect(BashExecutionToolDescription).toContain(
|
|
25
|
+
'failed executions do not register new files'
|
|
26
|
+
);
|
|
27
|
+
expect(BashExecutionToolDescription).toContain('not later-call storage');
|
|
28
|
+
});
|
|
29
|
+
|
|
21
30
|
it('appends the tool-output references guide when enabled', () => {
|
|
22
31
|
const composed = buildBashExecutionToolDescription({
|
|
23
32
|
enableToolOutputReferences: true,
|
|
@@ -165,6 +165,17 @@ describe('CodeAPI auth header injection', () => {
|
|
|
165
165
|
).not.toHaveProperty('authHeaders');
|
|
166
166
|
});
|
|
167
167
|
|
|
168
|
+
it('tolerates null params for direct code execution', async () => {
|
|
169
|
+
fetchMock.mockResolvedValueOnce(
|
|
170
|
+
jsonResponse({ session_id: 'session_123', stdout: '1\n' })
|
|
171
|
+
);
|
|
172
|
+
const tool = createCodeExecutionTool(null);
|
|
173
|
+
|
|
174
|
+
await expect(
|
|
175
|
+
tool.invoke({ lang: 'py', code: 'print(1)' })
|
|
176
|
+
).resolves.toBeDefined();
|
|
177
|
+
});
|
|
178
|
+
|
|
168
179
|
it('forwards Authorization for bash execution', async () => {
|
|
169
180
|
fetchMock.mockResolvedValueOnce(
|
|
170
181
|
jsonResponse({ session_id: 'session_123', stdout: '1\n' })
|
|
@@ -333,6 +344,38 @@ describe('CodeAPI auth header injection', () => {
|
|
|
333
344
|
);
|
|
334
345
|
});
|
|
335
346
|
|
|
347
|
+
it('reminds that failed bash programmatic executions do not register new files', async () => {
|
|
348
|
+
fetchMock.mockResolvedValueOnce(
|
|
349
|
+
jsonResponse({
|
|
350
|
+
status: 'error',
|
|
351
|
+
error: 'jq failed',
|
|
352
|
+
stderr: 'jq: Cannot index string with string "name"',
|
|
353
|
+
})
|
|
354
|
+
);
|
|
355
|
+
const tool = createBashProgrammaticToolCallingTool();
|
|
356
|
+
|
|
357
|
+
await expect(
|
|
358
|
+
tool.invoke(
|
|
359
|
+
{
|
|
360
|
+
code: [
|
|
361
|
+
'lookup_user "{}" > /mnt/data/user.json',
|
|
362
|
+
'jq -r \'.result.name\' /mnt/data/user.json',
|
|
363
|
+
].join('\n'),
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
toolCall: {
|
|
367
|
+
name: 'bash_programmatic_code_execution',
|
|
368
|
+
args: {},
|
|
369
|
+
toolMap: toolMap(),
|
|
370
|
+
toolDefs,
|
|
371
|
+
},
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
).rejects.toThrow(
|
|
375
|
+
'files written during this failed call were not registered for later calls'
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
|
|
336
379
|
it('fetches session files with the CodeAPI resource scope and auth headers', async () => {
|
|
337
380
|
fetchMock.mockResolvedValueOnce(
|
|
338
381
|
jsonResponse([
|
|
@@ -8,6 +8,7 @@ import type * as t from '@/types';
|
|
|
8
8
|
import { Constants } from '@/common';
|
|
9
9
|
import {
|
|
10
10
|
createProgrammaticToolCallingTool,
|
|
11
|
+
createProgrammaticToolCallingSchema,
|
|
11
12
|
formatCompletedResponse,
|
|
12
13
|
extractUsedToolNames,
|
|
13
14
|
filterToolsByUsage,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
normalizeToPythonIdentifier,
|
|
16
17
|
unwrapToolResponse,
|
|
17
18
|
} from '../ProgrammaticToolCalling';
|
|
19
|
+
import { createBashProgrammaticToolCallingSchema } from '../BashProgrammaticToolCalling';
|
|
18
20
|
import {
|
|
19
21
|
createProgrammaticToolRegistry,
|
|
20
22
|
createGetTeamMembersTool,
|
|
@@ -24,6 +26,33 @@ import {
|
|
|
24
26
|
} from '@/test/mockTools';
|
|
25
27
|
|
|
26
28
|
describe('ProgrammaticToolCalling', () => {
|
|
29
|
+
describe('tool descriptions', () => {
|
|
30
|
+
it('explains Python inner-tool call and result shape', () => {
|
|
31
|
+
const schema = createProgrammaticToolCallingSchema();
|
|
32
|
+
const description = schema.properties.code.description;
|
|
33
|
+
|
|
34
|
+
expect(description).toContain('keyword args only');
|
|
35
|
+
expect(description).toContain('never pass a dict');
|
|
36
|
+
expect(description).toContain('Tool results are decoded Python values');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('explains bash inner-tool stdout shape', () => {
|
|
40
|
+
const schema = createBashProgrammaticToolCallingSchema();
|
|
41
|
+
const description = schema.properties.code.description;
|
|
42
|
+
|
|
43
|
+
expect(description).toContain('jq: use fromjson? // .');
|
|
44
|
+
expect(description).toContain('again on JSON-string fields');
|
|
45
|
+
expect(description).toContain('arrays may contain strings');
|
|
46
|
+
expect(description).toContain('raw=$(tool');
|
|
47
|
+
expect(description).toContain('direct tool > file may be empty');
|
|
48
|
+
expect(description).toContain('/mnt/data/sf.json');
|
|
49
|
+
expect(description).toContain(
|
|
50
|
+
'failed executions do not register new files'
|
|
51
|
+
);
|
|
52
|
+
expect(description).toContain('not later-call storage');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
27
56
|
describe('executeTools', () => {
|
|
28
57
|
let toolMap: t.ToolMap;
|
|
29
58
|
|
|
@@ -656,13 +685,26 @@ for member in team:
|
|
|
656
685
|
expect(output).toContain('stderr:\nWarning: deprecated function');
|
|
657
686
|
});
|
|
658
687
|
|
|
659
|
-
it('
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
688
|
+
it('adds a /tmp scratch reminder when source code used /tmp', () => {
|
|
689
|
+
const response: t.ProgrammaticExecutionResponse = {
|
|
690
|
+
status: 'completed',
|
|
691
|
+
stdout: 'done\n',
|
|
692
|
+
stderr: '',
|
|
693
|
+
files: [],
|
|
694
|
+
session_id: 'sess_abc123',
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
const [output] = formatCompletedResponse(
|
|
698
|
+
response,
|
|
699
|
+
'tool "{}" > /tmp/result.json'
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
expect(output).toContain('stdout:\ndone');
|
|
703
|
+
expect(output).toContain('/tmp files are same-call scratch only');
|
|
704
|
+
expect(output).toContain('use /mnt/data for files needed later');
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
it('preserves files on the artifact and summarizes them without listing paths', () => {
|
|
666
708
|
const response: t.ProgrammaticExecutionResponse = {
|
|
667
709
|
status: 'completed',
|
|
668
710
|
stdout: 'Generated report\n',
|
|
@@ -677,9 +719,12 @@ for member in team:
|
|
|
677
719
|
|
|
678
720
|
const [output, artifact] = formatCompletedResponse(response);
|
|
679
721
|
|
|
680
|
-
/* Tool result text is stdout/stderr only. */
|
|
681
722
|
expect(output).toContain('stdout:\nGenerated report');
|
|
682
|
-
expect(output).
|
|
723
|
+
expect(output).toContain('Generated files:');
|
|
724
|
+
expect(output).toContain(
|
|
725
|
+
'Session files: 2 persisted file(s) are available in /mnt/data, including 0 image(s).'
|
|
726
|
+
);
|
|
727
|
+
expect(output).toContain('do not invent download links');
|
|
683
728
|
expect(output).not.toContain('Available files');
|
|
684
729
|
expect(output).not.toContain('report.pdf');
|
|
685
730
|
expect(output).not.toContain('SKILL.md');
|
|
@@ -691,6 +736,25 @@ for member in team:
|
|
|
691
736
|
expect(artifact.files).toHaveLength(3);
|
|
692
737
|
expect(artifact.files).toEqual(response.files);
|
|
693
738
|
});
|
|
739
|
+
|
|
740
|
+
it('omits the generated-file summary for inherited-only files', () => {
|
|
741
|
+
const response: t.ProgrammaticExecutionResponse = {
|
|
742
|
+
status: 'completed',
|
|
743
|
+
stdout: 'No new files\n',
|
|
744
|
+
stderr: '',
|
|
745
|
+
files: [
|
|
746
|
+
{ id: 'i1', name: 'skills/SKILL.md', inherited: true },
|
|
747
|
+
{ id: 'i2', name: 'inputs/source.csv', inherited: true },
|
|
748
|
+
],
|
|
749
|
+
session_id: 'sess_abc123',
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
const [output, artifact] = formatCompletedResponse(response);
|
|
753
|
+
|
|
754
|
+
expect(output).toBe('stdout:\nNo new files');
|
|
755
|
+
expect(output).not.toContain('Generated files:');
|
|
756
|
+
expect(artifact.files).toEqual(response.files);
|
|
757
|
+
});
|
|
694
758
|
});
|
|
695
759
|
|
|
696
760
|
describe('createProgrammaticToolCallingTool - Manual Invocation', () => {
|
|
@@ -889,11 +953,7 @@ for member in team:
|
|
|
889
953
|
});
|
|
890
954
|
});
|
|
891
955
|
|
|
892
|
-
it('
|
|
893
|
-
/* Output stays stdout/stderr-only regardless of file count or
|
|
894
|
-
* filename shape. The artifact is the sole sink for file refs;
|
|
895
|
-
* hosts thread them into `_injected_files` on subsequent
|
|
896
|
-
* tool calls via `storeCodeSessionFromResults`. */
|
|
956
|
+
it('summarizes files in output while keeping exact refs on the artifact', () => {
|
|
897
957
|
const response: t.ProgrammaticExecutionResponse = {
|
|
898
958
|
status: 'completed',
|
|
899
959
|
stdout: 'Report generated\n',
|
|
@@ -909,14 +969,38 @@ for member in team:
|
|
|
909
969
|
|
|
910
970
|
const [output, artifact] = formatCompletedResponse(response);
|
|
911
971
|
|
|
912
|
-
expect(output).
|
|
972
|
+
expect(output).toContain('stdout:\nReport generated');
|
|
973
|
+
expect(output).toContain(
|
|
974
|
+
'Session files: 4 persisted file(s) are available in /mnt/data, including 1 image(s).'
|
|
975
|
+
);
|
|
913
976
|
expect(output).not.toContain('report.csv');
|
|
914
977
|
expect(output).not.toContain('chart.png');
|
|
915
|
-
expect(output).not.toContain('/mnt/data/');
|
|
916
978
|
|
|
917
979
|
expect(artifact.files).toHaveLength(4);
|
|
918
980
|
expect(artifact.files).toEqual(response.files);
|
|
919
981
|
});
|
|
982
|
+
|
|
983
|
+
it('treats malformed file refs as non-image files', () => {
|
|
984
|
+
const malformedFile = { id: 'broken' } as t.FileRef;
|
|
985
|
+
const response: t.ProgrammaticExecutionResponse = {
|
|
986
|
+
status: 'completed',
|
|
987
|
+
stdout: 'Report generated\n',
|
|
988
|
+
stderr: '',
|
|
989
|
+
files: [
|
|
990
|
+
{ id: '1', name: 'chart.png' },
|
|
991
|
+
malformedFile,
|
|
992
|
+
{ id: '3', name: 'inherited.png', inherited: true },
|
|
993
|
+
],
|
|
994
|
+
session_id: 'sess_xyz',
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const [output, artifact] = formatCompletedResponse(response);
|
|
998
|
+
|
|
999
|
+
expect(output).toContain(
|
|
1000
|
+
'Session files: 2 persisted file(s) are available in /mnt/data, including 1 image(s).'
|
|
1001
|
+
);
|
|
1002
|
+
expect(artifact.files).toEqual(response.files);
|
|
1003
|
+
});
|
|
920
1004
|
});
|
|
921
1005
|
|
|
922
1006
|
describe('Tool Data Extraction', () => {
|