@librechat/agents 3.1.73 → 3.1.75-dev.0

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 (159) hide show
  1. package/README.md +66 -0
  2. package/dist/cjs/agents/AgentContext.cjs +146 -57
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +13 -3
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/llm/anthropic/index.cjs +145 -52
  7. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  8. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  9. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +25 -15
  10. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  11. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +84 -70
  12. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  13. package/dist/cjs/llm/bedrock/index.cjs +1 -1
  14. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  15. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +213 -3
  16. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  17. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs +2 -1
  18. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +1 -1
  19. package/dist/cjs/llm/google/utils/common.cjs +5 -4
  20. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  21. package/dist/cjs/llm/openai/index.cjs +468 -647
  22. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  23. package/dist/cjs/llm/openai/utils/index.cjs +1 -448
  24. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  25. package/dist/cjs/llm/openrouter/index.cjs +57 -175
  26. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  27. package/dist/cjs/llm/vertexai/index.cjs +5 -3
  28. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  29. package/dist/cjs/main.cjs +1 -0
  30. package/dist/cjs/main.cjs.map +1 -1
  31. package/dist/cjs/messages/cache.cjs +39 -4
  32. package/dist/cjs/messages/cache.cjs.map +1 -1
  33. package/dist/cjs/messages/core.cjs +7 -6
  34. package/dist/cjs/messages/core.cjs.map +1 -1
  35. package/dist/cjs/messages/format.cjs +7 -6
  36. package/dist/cjs/messages/format.cjs.map +1 -1
  37. package/dist/cjs/messages/langchain.cjs +26 -0
  38. package/dist/cjs/messages/langchain.cjs.map +1 -0
  39. package/dist/cjs/messages/prune.cjs +7 -6
  40. package/dist/cjs/messages/prune.cjs.map +1 -1
  41. package/dist/cjs/tools/BashExecutor.cjs +21 -11
  42. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  43. package/dist/cjs/tools/CodeExecutor.cjs +37 -10
  44. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  45. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +16 -11
  46. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  47. package/dist/cjs/tools/ToolNode.cjs +5 -1
  48. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  49. package/dist/esm/agents/AgentContext.mjs +147 -58
  50. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  51. package/dist/esm/graphs/Graph.mjs +13 -3
  52. package/dist/esm/graphs/Graph.mjs.map +1 -1
  53. package/dist/esm/llm/anthropic/index.mjs +146 -54
  54. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  55. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  56. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +25 -15
  57. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  58. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +84 -71
  59. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  60. package/dist/esm/llm/bedrock/index.mjs +1 -1
  61. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  62. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +214 -4
  63. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  64. package/dist/esm/llm/bedrock/utils/message_outputs.mjs +2 -1
  65. package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +1 -1
  66. package/dist/esm/llm/google/utils/common.mjs +5 -4
  67. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  68. package/dist/esm/llm/openai/index.mjs +469 -648
  69. package/dist/esm/llm/openai/index.mjs.map +1 -1
  70. package/dist/esm/llm/openai/utils/index.mjs +4 -449
  71. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  72. package/dist/esm/llm/openrouter/index.mjs +57 -175
  73. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  74. package/dist/esm/llm/vertexai/index.mjs +5 -3
  75. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  76. package/dist/esm/main.mjs +1 -1
  77. package/dist/esm/messages/cache.mjs +39 -4
  78. package/dist/esm/messages/cache.mjs.map +1 -1
  79. package/dist/esm/messages/core.mjs +7 -6
  80. package/dist/esm/messages/core.mjs.map +1 -1
  81. package/dist/esm/messages/format.mjs +7 -6
  82. package/dist/esm/messages/format.mjs.map +1 -1
  83. package/dist/esm/messages/langchain.mjs +23 -0
  84. package/dist/esm/messages/langchain.mjs.map +1 -0
  85. package/dist/esm/messages/prune.mjs +7 -6
  86. package/dist/esm/messages/prune.mjs.map +1 -1
  87. package/dist/esm/tools/BashExecutor.mjs +22 -12
  88. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  89. package/dist/esm/tools/CodeExecutor.mjs +37 -11
  90. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  91. package/dist/esm/tools/ProgrammaticToolCalling.mjs +17 -12
  92. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  93. package/dist/esm/tools/ToolNode.mjs +5 -1
  94. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  95. package/dist/types/agents/AgentContext.d.ts +29 -4
  96. package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +46 -0
  97. package/dist/types/llm/anthropic/index.d.ts +22 -9
  98. package/dist/types/llm/anthropic/types.d.ts +5 -1
  99. package/dist/types/llm/anthropic/utils/message_outputs.d.ts +13 -6
  100. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +1 -1
  101. package/dist/types/llm/openai/index.d.ts +21 -24
  102. package/dist/types/llm/openrouter/index.d.ts +11 -9
  103. package/dist/types/llm/vertexai/index.d.ts +1 -0
  104. package/dist/types/messages/cache.d.ts +4 -1
  105. package/dist/types/messages/langchain.d.ts +27 -0
  106. package/dist/types/tools/CodeExecutor.d.ts +6 -0
  107. package/dist/types/types/graph.d.ts +26 -38
  108. package/dist/types/types/llm.d.ts +3 -3
  109. package/dist/types/types/run.d.ts +2 -0
  110. package/dist/types/types/stream.d.ts +1 -1
  111. package/dist/types/types/tools.d.ts +9 -0
  112. package/package.json +17 -16
  113. package/src/agents/AgentContext.ts +189 -71
  114. package/src/agents/__tests__/AgentContext.anthropic.live.test.ts +116 -0
  115. package/src/agents/__tests__/AgentContext.bedrock.live.test.ts +149 -0
  116. package/src/agents/__tests__/AgentContext.test.ts +333 -2
  117. package/src/agents/__tests__/promptCacheLiveHelpers.ts +165 -0
  118. package/src/graphs/Graph.ts +24 -4
  119. package/src/graphs/__tests__/composition.smoke.test.ts +188 -0
  120. package/src/llm/anthropic/index.ts +252 -84
  121. package/src/llm/anthropic/llm.spec.ts +751 -102
  122. package/src/llm/anthropic/types.ts +9 -1
  123. package/src/llm/anthropic/utils/message_inputs.ts +43 -20
  124. package/src/llm/anthropic/utils/message_outputs.ts +119 -101
  125. package/src/llm/anthropic/utils/server-tool-inputs.test.ts +77 -0
  126. package/src/llm/bedrock/index.ts +2 -2
  127. package/src/llm/bedrock/llm.spec.ts +341 -0
  128. package/src/llm/bedrock/utils/message_inputs.ts +303 -4
  129. package/src/llm/bedrock/utils/message_outputs.ts +2 -1
  130. package/src/llm/custom-chat-models.smoke.test.ts +662 -0
  131. package/src/llm/google/llm.spec.ts +339 -57
  132. package/src/llm/google/utils/common.ts +53 -48
  133. package/src/llm/openai/contentBlocks.test.ts +346 -0
  134. package/src/llm/openai/index.ts +736 -837
  135. package/src/llm/openai/utils/index.ts +84 -64
  136. package/src/llm/openrouter/index.ts +124 -247
  137. package/src/llm/openrouter/reasoning.test.ts +8 -1
  138. package/src/llm/vertexai/index.ts +11 -5
  139. package/src/llm/vertexai/llm.spec.ts +28 -1
  140. package/src/messages/cache.test.ts +106 -4
  141. package/src/messages/cache.ts +57 -5
  142. package/src/messages/core.ts +16 -9
  143. package/src/messages/format.ts +9 -6
  144. package/src/messages/langchain.ts +39 -0
  145. package/src/messages/prune.ts +12 -8
  146. package/src/scripts/caching.ts +2 -3
  147. package/src/specs/anthropic.simple.test.ts +61 -0
  148. package/src/specs/summarization.test.ts +58 -61
  149. package/src/tools/BashExecutor.ts +37 -13
  150. package/src/tools/CodeExecutor.ts +55 -11
  151. package/src/tools/ProgrammaticToolCalling.ts +29 -14
  152. package/src/tools/ToolNode.ts +5 -1
  153. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +60 -0
  154. package/src/types/graph.ts +35 -88
  155. package/src/types/llm.ts +3 -3
  156. package/src/types/run.ts +2 -0
  157. package/src/types/stream.ts +1 -1
  158. package/src/types/tools.ts +9 -0
  159. package/src/utils/llmConfig.ts +1 -6
@@ -3,17 +3,23 @@ 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 { imageExtRegex, getCodeBaseURL } from './CodeExecutor';
6
+ import { getCodeBaseURL, renderFileSection } from './CodeExecutor';
7
7
  import { Constants } from '@/common';
8
8
 
9
9
  config();
10
10
 
11
- const imageMessage = 'Image is already displayed to the user';
12
11
  const otherMessage = 'File is already downloaded by the user';
12
+ const inheritedFileMessage =
13
+ 'Available as an input — already known to the user';
13
14
  const accessMessage =
14
15
  'Note: Files from previous executions are automatically available and can be modified.';
15
16
  const emptyOutputMessage =
16
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.';
17
23
 
18
24
  const baseEndpoint = getCodeBaseURL();
19
25
  const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
@@ -198,20 +204,38 @@ function createBashExecutionTool(
198
204
  }
199
205
  if (result.stderr) formattedOutput += `stderr:\n${result.stderr}\n`;
200
206
  if (result.files && result.files.length > 0) {
201
- formattedOutput += 'Generated files:\n';
207
+ /* Split inherited (read-only / unchanged-input passthroughs from
208
+ * codeapi) from genuine generated outputs. The LLM was previously
209
+ * shown skill files under "Generated files:" with the message
210
+ * "File is already downloaded by the user", which led it to
211
+ * (a) believe it had just produced files it merely referenced
212
+ * and (b) sometimes invent paths like /mnt/user-data/uploads/
213
+ * trying to find the "originals". Labeling them as inputs makes
214
+ * the mental model accurate. */
215
+ const inheritedFiles = result.files.filter(
216
+ (f) => f.inherited === true
217
+ );
218
+ const generatedFiles = result.files.filter(
219
+ (f) => f.inherited !== true
220
+ );
202
221
 
203
- const fileCount = result.files.length;
204
- for (let i = 0; i < fileCount; i++) {
205
- const file = result.files[i];
206
- const isImage = imageExtRegex.test(file.name);
207
- formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;
222
+ formattedOutput += renderFileSection(
223
+ generatedFilesHeader,
224
+ generatedFiles,
225
+ otherMessage
226
+ );
227
+ formattedOutput += renderFileSection(
228
+ inheritedFilesHeader,
229
+ inheritedFiles,
230
+ inheritedFileMessage
231
+ );
208
232
 
209
- if (i < fileCount - 1) {
210
- formattedOutput += fileCount <= 3 ? ', ' : ',\n';
211
- }
233
+ if (generatedFiles.length > 0) {
234
+ formattedOutput += `\n\n${accessMessage}`;
235
+ }
236
+ if (inheritedFiles.length > 0) {
237
+ formattedOutput += `\n\n${inheritedNote}`;
212
238
  }
213
-
214
- formattedOutput += `\n\n${accessMessage}`;
215
239
  return [
216
240
  formattedOutput.trim(),
217
241
  {
@@ -15,10 +15,41 @@ export const getCodeBaseURL = (): string =>
15
15
 
16
16
  const imageMessage = 'Image is already displayed to the user';
17
17
  const otherMessage = 'File is already downloaded by the user';
18
+ const inheritedFileMessage =
19
+ 'Available as an input — already known to the user';
18
20
  const accessMessage =
19
21
  'Note: Files from previous executions are automatically available and can be modified.';
20
22
  const emptyOutputMessage =
21
23
  '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
+ }
22
53
 
23
54
  const SUPPORTED_LANGUAGES = [
24
55
  'py',
@@ -196,20 +227,33 @@ function createCodeExecutionTool(
196
227
  }
197
228
  if (result.stderr) formattedOutput += `stderr:\n${result.stderr}\n`;
198
229
  if (result.files && result.files.length > 0) {
199
- formattedOutput += 'Generated files:\n';
230
+ /* See BashExecutor for the rationale: split inherited (read-only
231
+ * passthrough) inputs from real generated outputs so the LLM
232
+ * doesn't conflate skill files with newly-produced artifacts. */
233
+ const inheritedFiles = result.files.filter(
234
+ (f) => f.inherited === true
235
+ );
236
+ const generatedFiles = result.files.filter(
237
+ (f) => f.inherited !== true
238
+ );
200
239
 
201
- const fileCount = result.files.length;
202
- for (let i = 0; i < fileCount; i++) {
203
- const file = result.files[i];
204
- const isImage = imageExtRegex.test(file.name);
205
- formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;
240
+ formattedOutput += renderFileSection(
241
+ generatedFilesHeader,
242
+ generatedFiles,
243
+ otherMessage
244
+ );
245
+ formattedOutput += renderFileSection(
246
+ inheritedFilesHeader,
247
+ inheritedFiles,
248
+ inheritedFileMessage
249
+ );
206
250
 
207
- if (i < fileCount - 1) {
208
- formattedOutput += fileCount <= 3 ? ', ' : ',\n';
209
- }
251
+ if (generatedFiles.length > 0) {
252
+ formattedOutput += `\n\n${accessMessage}`;
253
+ }
254
+ if (inheritedFiles.length > 0) {
255
+ formattedOutput += `\n\n${inheritedNote}`;
210
256
  }
211
-
212
- formattedOutput += `\n\n${accessMessage}`;
213
257
  return [
214
258
  formattedOutput.trim(),
215
259
  {
@@ -5,7 +5,7 @@ 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 { imageExtRegex, getCodeBaseURL } from './CodeExecutor';
8
+ import { getCodeBaseURL, renderFileSection } from './CodeExecutor';
9
9
  import { Constants } from '@/common';
10
10
 
11
11
  config();
@@ -14,8 +14,14 @@ config();
14
14
  // Constants
15
15
  // ============================================================================
16
16
 
17
- const imageMessage = 'Image is already displayed to the user';
18
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.';
19
25
  const accessMessage =
20
26
  'Note: Files from previous executions are automatically available and can be modified.';
21
27
  const emptyOutputMessage =
@@ -552,20 +558,29 @@ export function formatCompletedResponse(
552
558
  }
553
559
 
554
560
  if (response.files && response.files.length > 0) {
555
- formatted += 'Generated files:\n';
556
-
557
- const fileCount = response.files.length;
558
- for (let i = 0; i < fileCount; i++) {
559
- const file = response.files[i];
560
- const isImage = imageExtRegex.test(file.name);
561
- formatted += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;
561
+ /* See BashExecutor for the rationale: split inherited (read-only
562
+ * passthrough) inputs from real generated outputs so the LLM doesn't
563
+ * conflate skill files with newly-produced artifacts. */
564
+ const inheritedFiles = response.files.filter((f) => f.inherited === true);
565
+ const generatedFiles = response.files.filter((f) => f.inherited !== true);
566
+
567
+ formatted += renderFileSection(
568
+ generatedFilesHeader,
569
+ generatedFiles,
570
+ otherMessage
571
+ );
572
+ formatted += renderFileSection(
573
+ inheritedFilesHeader,
574
+ inheritedFiles,
575
+ inheritedFileMessage
576
+ );
562
577
 
563
- if (i < fileCount - 1) {
564
- formatted += fileCount <= 3 ? ', ' : ',\n';
565
- }
578
+ if (generatedFiles.length > 0) {
579
+ formatted += `\n\n${accessMessage}`;
580
+ }
581
+ if (inheritedFiles.length > 0) {
582
+ formatted += `\n\n${inheritedNote}`;
566
583
  }
567
-
568
- formatted += `\n\n${accessMessage}`;
569
584
  }
570
585
 
571
586
  return [
@@ -33,6 +33,7 @@ import {
33
33
  } from '@/utils/truncation';
34
34
  import { safeDispatchCustomEvent } from '@/utils/events';
35
35
  import { executeHooks } from '@/hooks';
36
+ import { toLangChainContent } from '@/messages/langchain';
36
37
  import { Constants, GraphEvents, CODE_EXECUTION_TOOLS } from '@/common';
37
38
  import {
38
39
  buildReferenceKey,
@@ -1282,7 +1283,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1282
1283
  if (msg.skillName != null) additional_kwargs.skillName = msg.skillName;
1283
1284
 
1284
1285
  converted.push(
1285
- new HumanMessage({ content: msg.content, additional_kwargs })
1286
+ new HumanMessage({
1287
+ content: toLangChainContent(msg.content),
1288
+ additional_kwargs,
1289
+ })
1286
1290
  );
1287
1291
  }
1288
1292
  return converted;
@@ -664,6 +664,66 @@ for member in team:
664
664
  expect(output).toContain('chart.png');
665
665
  expect(output).toContain('Image is already displayed to the user');
666
666
  });
667
+
668
+ it('splits inherited inputs from generated outputs into distinct sections', () => {
669
+ const response: t.ProgrammaticExecutionResponse = {
670
+ status: 'completed',
671
+ stdout: 'analysis done\n',
672
+ stderr: '',
673
+ files: [
674
+ { id: 'g1', name: 'report.pdf' },
675
+ { id: 'i1', name: 'pptx/SKILL.md', inherited: true },
676
+ { id: 'i2', name: 'pptx/scripts/clean.py', inherited: true },
677
+ { id: 'g2', name: 'chart.png' },
678
+ ],
679
+ session_id: 'sess_abc123',
680
+ };
681
+
682
+ const [output, artifact] = formatCompletedResponse(response);
683
+
684
+ /* Generated section lists only outputs the run produced. */
685
+ const generatedIdx = output.indexOf('Generated files:');
686
+ const inheritedIdx = output.indexOf('Available files (inputs');
687
+ expect(generatedIdx).toBeGreaterThan(-1);
688
+ expect(inheritedIdx).toBeGreaterThan(generatedIdx);
689
+
690
+ /* Slice each section so we can assert membership without
691
+ * cross-talk between the two listings. */
692
+ const generatedSection = output.slice(generatedIdx, inheritedIdx);
693
+ const inheritedSection = output.slice(inheritedIdx);
694
+
695
+ expect(generatedSection).toContain('report.pdf');
696
+ expect(generatedSection).toContain('chart.png');
697
+ expect(generatedSection).not.toContain('SKILL.md');
698
+
699
+ expect(inheritedSection).toContain('pptx/SKILL.md');
700
+ expect(inheritedSection).toContain('pptx/scripts/clean.py');
701
+ expect(inheritedSection).toContain('Available as an input');
702
+
703
+ /* The artifact still carries every file so the host can still
704
+ * thread per-file ids through to subsequent calls. */
705
+ expect(artifact.files).toHaveLength(4);
706
+ });
707
+
708
+ it('omits the Generated files header when every entry is inherited', () => {
709
+ const response: t.ProgrammaticExecutionResponse = {
710
+ status: 'completed',
711
+ stdout: 'cat: ok\n',
712
+ stderr: '',
713
+ files: [
714
+ { id: 'i1', name: 'pptx/SKILL.md', inherited: true },
715
+ { id: 'i2', name: 'pptx/editing.md', inherited: true },
716
+ ],
717
+ session_id: 'sess_abc123',
718
+ };
719
+
720
+ const [output] = formatCompletedResponse(response);
721
+
722
+ expect(output).not.toContain('Generated files:');
723
+ expect(output).toContain('Available files (inputs');
724
+ expect(output).toContain('pptx/SKILL.md');
725
+ expect(output).toContain('pptx/editing.md');
726
+ });
667
727
  });
668
728
 
669
729
  describe('createProgrammaticToolCallingTool - Manual Invocation', () => {
@@ -1,14 +1,5 @@
1
1
  // src/types/graph.ts
2
- import type {
3
- START,
4
- StateType,
5
- UpdateType,
6
- StateGraph,
7
- StateGraphArgs,
8
- StateDefinition,
9
- CompiledStateGraph,
10
- BinaryOperatorAggregate,
11
- } from '@langchain/langgraph';
2
+ import type { START, StateGraph, StateGraphArgs } from '@langchain/langgraph';
12
3
  import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
13
4
  import type {
14
5
  BaseMessage,
@@ -129,76 +120,40 @@ export type Workflow<
129
120
  N extends string = string,
130
121
  > = StateGraph<T, U, N>;
131
122
 
132
- export type CompiledWorkflow<
133
- T extends BaseGraphState = BaseGraphState,
134
- U extends Partial<T> = Partial<T>,
135
- N extends string = string,
136
- > = CompiledStateGraph<T, U, N>;
137
-
138
- export type CompiledStateWorkflow = CompiledStateGraph<
139
- StateType<{
140
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
141
- }>,
142
- UpdateType<{
143
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
144
- }>,
145
- string,
146
- {
147
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
148
- },
149
- {
150
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
151
- },
152
- StateDefinition
123
+ type LangChainEventStreamCallbackHandlerInput = NonNullable<
124
+ Parameters<Runnable['streamEvents']>[2]
153
125
  >;
154
126
 
155
- export type CompiledMultiAgentWorkflow = CompiledStateGraph<
156
- StateType<{
157
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
158
- agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
159
- }>,
160
- UpdateType<{
161
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
162
- agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
163
- }>,
164
- string,
165
- {
166
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
167
- agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
168
- },
169
- {
170
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
171
- agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
172
- },
173
- StateDefinition
174
- >;
127
+ export type EventStreamCallbackHandlerInput =
128
+ LangChainEventStreamCallbackHandlerInput & {
129
+ autoClose?: boolean;
130
+ raiseError?: boolean;
131
+ ignoreCustomEvent?: boolean;
132
+ };
175
133
 
176
- export type CompiledAgentWorfklow = CompiledStateGraph<
134
+ export type WorkflowValuesStreamConfig = RunnableConfig & {
135
+ streamMode: 'values';
136
+ };
137
+
138
+ /**
139
+ * LangGraph stream output is mode-dependent (`values`, `updates`, SSE, etc.).
140
+ * Keep the base Runnable stream output as unknown and narrow at callsites that
141
+ * choose a concrete streamMode.
142
+ */
143
+ export type CompiledWorkflow<
144
+ TInput extends BaseGraphState = BaseGraphState,
145
+ TOutput extends BaseGraphState = TInput,
146
+ > = Omit<Runnable<TInput, unknown>, 'invoke'> & {
147
+ invoke(input: TInput, config?: RunnableConfig): Promise<TOutput>;
148
+ };
149
+
150
+ export type CompiledStateWorkflow = CompiledWorkflow;
151
+
152
+ export type CompiledMultiAgentWorkflow = CompiledWorkflow<MultiAgentGraphState>;
153
+
154
+ export type CompiledAgentWorfklow = CompiledWorkflow<
177
155
  AgentSubgraphState,
178
- Partial<AgentSubgraphState>,
179
- '__start__' | `agent=${string}` | `tools=${string}` | `summarize=${string}`,
180
- {
181
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
182
- summarizationRequest: BinaryOperatorAggregate<
183
- SummarizationNodeInput | undefined,
184
- SummarizationNodeInput | undefined
185
- >;
186
- },
187
- {
188
- messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
189
- summarizationRequest: BinaryOperatorAggregate<
190
- SummarizationNodeInput | undefined,
191
- SummarizationNodeInput | undefined
192
- >;
193
- },
194
- StateDefinition,
195
- {
196
- [x: `agent=${string}`]: Partial<BaseGraphState>;
197
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
198
- [x: `tools=${string}`]: any;
199
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
200
- [x: `summarize=${string}`]: any;
201
- }
156
+ AgentSubgraphState
202
157
  >;
203
158
 
204
159
  export type SystemRunnable =
@@ -214,21 +169,11 @@ export type SystemRunnable =
214
169
  * These are intentionally untyped to avoid coupling to library internals.
215
170
  */
216
171
  export type CompileOptions = {
217
- // A checkpointer instance (e.g., MemorySaver, SQL saver)
218
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
219
- checkpointer?: any;
172
+ checkpointer?: unknown;
220
173
  interruptBefore?: string[];
221
174
  interruptAfter?: string[];
222
175
  };
223
176
 
224
- export type EventStreamCallbackHandlerInput =
225
- Parameters<CompiledWorkflow['streamEvents']>[2] extends Omit<
226
- infer T,
227
- 'autoClose'
228
- >
229
- ? T
230
- : never;
231
-
232
177
  export type StreamChunk =
233
178
  | (ChatGenerationChunk & {
234
179
  message: AIMessageChunk;
@@ -471,10 +416,12 @@ export interface AgentInputs {
471
416
  toolMap?: ToolMap;
472
417
  tools?: GraphTools;
473
418
  provider: Providers;
419
+ /** Stable/cacheable system instructions. */
474
420
  instructions?: string;
475
421
  streamBuffer?: number;
476
422
  maxContextTokens?: number;
477
423
  clientOptions?: ClientOptions;
424
+ /** Dynamic system tail appended after stable instructions without provider cache markers. */
478
425
  additional_instructions?: string;
479
426
  reasoningKey?: 'reasoning_content' | 'reasoning';
480
427
  /** Format content blocks as strings (for legacy compatibility i.e. Ollama/Azure Serverless) */
@@ -500,7 +447,7 @@ export interface AgentInputs {
500
447
  summarizationEnabled?: boolean;
501
448
  summarizationConfig?: SummarizationConfig;
502
449
  /** Cross-run summary from a previous run, forwarded from formatAgentMessages.
503
- * Injected into the system message via AgentContext.buildInstructionsString(). */
450
+ * Injected into the dynamic system tail via AgentContext. */
504
451
  initialSummary?: { text: string; tokenCount: number };
505
452
  contextPruningConfig?: ContextPruningConfig;
506
453
  maxToolResultChars?: number;
package/src/types/llm.ts CHANGED
@@ -12,7 +12,7 @@ import type {
12
12
  } from '@langchain/openai';
13
13
  import type { GoogleGenerativeAIChatInput } from '@langchain/google-genai';
14
14
  import type { ChatVertexAIInput } from '@langchain/google-vertexai';
15
- import type { ChatDeepSeekCallOptions } from '@langchain/deepseek';
15
+ import type { ChatDeepSeekInput } from '@langchain/deepseek';
16
16
  import type { ChatOpenRouterCallOptions } from '@/llm/openrouter';
17
17
  import type { ChatBedrockConverseInput } from '@langchain/aws';
18
18
  import type { ChatMistralAIInput } from '@langchain/mistralai';
@@ -70,7 +70,7 @@ export type AnthropicReasoning = {
70
70
  export type GoogleThinkingConfig = {
71
71
  thinkingBudget?: number;
72
72
  includeThoughts?: boolean;
73
- thinkingLevel?: string;
73
+ thinkingLevel?: 'THINKING_LEVEL_UNSPECIFIED' | 'LOW' | 'MEDIUM' | 'HIGH';
74
74
  };
75
75
  export type OpenAIClientOptions = ChatOpenAIFields;
76
76
  export type AnthropicClientOptions = Omit<AnthropicInput, 'thinking'> & {
@@ -93,7 +93,7 @@ export type GoogleClientOptions = GoogleGenerativeAIChatInput & {
93
93
  customHeaders?: RequestOptions['customHeaders'];
94
94
  thinkingConfig?: GoogleThinkingConfig;
95
95
  };
96
- export type DeepSeekClientOptions = ChatDeepSeekCallOptions;
96
+ export type DeepSeekClientOptions = Partial<ChatDeepSeekInput>;
97
97
  export type XAIClientOptions = ChatXAIInput;
98
98
 
99
99
  export type ClientOptions =
package/src/types/run.ts CHANGED
@@ -75,7 +75,9 @@ export interface AgentStateChannels {
75
75
  messages: BaseMessage[];
76
76
  next: string;
77
77
  [key: string]: unknown;
78
+ /** Stable/cacheable system instructions. */
78
79
  instructions?: string;
80
+ /** Dynamic system tail appended after stable instructions. */
79
81
  additional_instructions?: string;
80
82
  }
81
83
 
@@ -191,7 +191,7 @@ export interface ExtendedMessageContent {
191
191
  type?: string;
192
192
  text?: string;
193
193
  input?: string;
194
- index?: number;
194
+ index?: string | number;
195
195
  id?: string;
196
196
  name?: string;
197
197
  }
@@ -113,6 +113,15 @@ export type FileRef = {
113
113
  path?: string;
114
114
  /** Session ID this file belongs to (for multi-session file tracking) */
115
115
  session_id?: string;
116
+ /**
117
+ * `true` when the codeapi sandbox echoed this entry as an unchanged
118
+ * passthrough of an input the caller already owns (skill files,
119
+ * downloaded inputs whose hash matched the baseline, inherited
120
+ * `.dirkeep` markers). The tool-result formatter renders these as
121
+ * "Available files" rather than "Generated files" so the LLM doesn't
122
+ * conflate infrastructure inputs with newly-produced outputs.
123
+ */
124
+ inherited?: true;
116
125
  };
117
126
 
118
127
  export type FileRefs = FileRef[];
@@ -56,9 +56,7 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
56
56
  provider: Providers.OPENROUTER,
57
57
  streaming: true,
58
58
  streamUsage: true,
59
- // model: 'anthropic/claude-sonnet-4',
60
- // model: 'moonshotai/kimi-k2-thinking',
61
- model: 'google/gemini-3-pro-preview',
59
+ model: process.env.OPENROUTER_MODEL ?? 'openai/gpt-4o-mini',
62
60
  apiKey: process.env.OPENROUTER_API_KEY,
63
61
  configuration: {
64
62
  baseURL: process.env.OPENROUTER_BASE_URL,
@@ -67,9 +65,6 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
67
65
  'X-Title': 'LibreChat',
68
66
  },
69
67
  },
70
- reasoning: {
71
- max_tokens: 8000,
72
- },
73
68
  modelKwargs: {
74
69
  max_tokens: 10000,
75
70
  },