deepagents 1.8.5 → 1.8.7

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/index.cjs CHANGED
@@ -106,6 +106,17 @@ var SandboxError = class SandboxError extends Error {
106
106
  return typeof error === "object" && error !== null && error[SANDBOX_ERROR_SYMBOL] === true;
107
107
  }
108
108
  };
109
+ /**
110
+ * Resolve a backend instance or await a {@link BackendFactory}.
111
+ *
112
+ * Accepts {@link BackendRuntime} or {@link ToolRuntime} — store typing differs
113
+ * between LangGraph checkpoint stores and core `ToolRuntime`; factories receive
114
+ * a value that is structurally compatible at runtime.
115
+ */
116
+ async function resolveBackend(backend, runtime) {
117
+ if (typeof backend === "function") return await backend(runtime);
118
+ return backend;
119
+ }
109
120
  //#endregion
110
121
  //#region src/backends/utils.ts
111
122
  /**
@@ -387,15 +398,15 @@ function grepMatchesFromFiles(files, pattern, path$9 = null, glob = null) {
387
398
  * for the middleware to apply via Command.
388
399
  */
389
400
  var StateBackend = class {
390
- stateAndStore;
391
- constructor(stateAndStore) {
392
- this.stateAndStore = stateAndStore;
401
+ runtime;
402
+ constructor(runtime) {
403
+ this.runtime = runtime;
393
404
  }
394
405
  /**
395
406
  * Get files from current state.
396
407
  */
397
408
  getFiles() {
398
- return this.stateAndStore.state.files || {};
409
+ return this.runtime.state.files || {};
399
410
  }
400
411
  /**
401
412
  * List files and directories in the specified directory (non-recursive).
@@ -647,6 +658,62 @@ indicate omitted lines in the middle of the content):
647
658
 
648
659
  {content_sample}`;
649
660
  /**
661
+ * Message template for evicted HumanMessages.
662
+ */
663
+ const TOO_LARGE_HUMAN_MSG = `Message content too large and was saved to the filesystem at: {file_path}
664
+
665
+ You can read the full content using the read_file tool with pagination (offset and limit parameters).
666
+
667
+ Here is a preview showing the head and tail of the content:
668
+
669
+ {content_sample}`;
670
+ /**
671
+ * Extract text content from a message.
672
+ *
673
+ * For string content, returns it directly. For array content (mixed block types
674
+ * like text + image), joins all text blocks. Returns empty string if no text found.
675
+ */
676
+ function extractTextFromMessage(message) {
677
+ if (typeof message.content === "string") return message.content;
678
+ if (Array.isArray(message.content)) return message.content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
679
+ return String(message.content);
680
+ }
681
+ /**
682
+ * Build replacement content for an evicted HumanMessage, preserving non-text blocks.
683
+ *
684
+ * For plain string content, returns the replacement text directly. For list content
685
+ * with mixed block types (e.g., text + image), replaces all text blocks with a single
686
+ * text block containing the replacement text while keeping non-text blocks intact.
687
+ */
688
+ function buildEvictedHumanContent(message, replacementText) {
689
+ if (typeof message.content === "string") return replacementText;
690
+ if (Array.isArray(message.content)) {
691
+ const mediaBlocks = message.content.filter((block) => typeof block === "object" && block !== null && block.type !== "text");
692
+ if (mediaBlocks.length === 0) return replacementText;
693
+ return [{
694
+ type: "text",
695
+ text: replacementText
696
+ }, ...mediaBlocks];
697
+ }
698
+ return replacementText;
699
+ }
700
+ /**
701
+ * Build a truncated HumanMessage for the model request.
702
+ *
703
+ * Computes a preview from the full content still in state and returns a
704
+ * lightweight replacement the model will see. Pure string computation — no
705
+ * backend I/O.
706
+ */
707
+ function buildTruncatedHumanMessage(message, filePath) {
708
+ const contentSample = createContentPreview(extractTextFromMessage(message));
709
+ return new langchain.HumanMessage({
710
+ content: buildEvictedHumanContent(message, TOO_LARGE_HUMAN_MSG.replace("{file_path}", filePath).replace("{content_sample}", contentSample)),
711
+ id: message.id,
712
+ additional_kwargs: { ...message.additional_kwargs },
713
+ response_metadata: { ...message.response_metadata }
714
+ });
715
+ }
716
+ /**
650
717
  * Create a preview of content showing head and tail with truncation marker.
651
718
  *
652
719
  * @param contentStr - The full content string to preview.
@@ -708,16 +775,6 @@ const FilesystemStateSchema = new _langchain_langgraph.StateSchema({ files: new
708
775
  inputSchema: zod_v4.z.record(zod_v4.z.string(), FileDataSchema.nullable()).optional(),
709
776
  reducer: fileDataReducer
710
777
  }) });
711
- /**
712
- * Resolve backend from factory or instance.
713
- *
714
- * @param backend - Backend instance or factory function
715
- * @param stateAndStore - State and store container for backend initialization
716
- */
717
- function getBackend(backend, stateAndStore) {
718
- if (typeof backend === "function") return backend(stateAndStore);
719
- return backend;
720
- }
721
778
  const FILESYSTEM_SYSTEM_PROMPT = `## Filesystem Tools \`ls\`, \`read_file\`, \`write_file\`, \`edit_file\`, \`glob\`, \`grep\`
722
779
 
723
780
  You have access to a filesystem which you can interact with using these tools.
@@ -835,11 +892,8 @@ Use this tool to run commands, scripts, tests, builds, and other shell operation
835
892
  */
836
893
  function createLsTool(backend, options) {
837
894
  const { customDescription } = options;
838
- return (0, langchain.tool)(async (input, config) => {
839
- const resolvedBackend = getBackend(backend, {
840
- state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
841
- store: config.store
842
- });
895
+ return (0, langchain.tool)(async (input, runtime) => {
896
+ const resolvedBackend = await resolveBackend(backend, runtime);
843
897
  const path = input.path || "/";
844
898
  const infos = await resolvedBackend.lsInfo(path);
845
899
  if (infos.length === 0) return `No files found in ${path}`;
@@ -863,11 +917,8 @@ function createLsTool(backend, options) {
863
917
  */
864
918
  function createReadFileTool(backend, options) {
865
919
  const { customDescription, toolTokenLimitBeforeEvict } = options;
866
- return (0, langchain.tool)(async (input, config) => {
867
- const resolvedBackend = getBackend(backend, {
868
- state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
869
- store: config.store
870
- });
920
+ return (0, langchain.tool)(async (input, runtime) => {
921
+ const resolvedBackend = await resolveBackend(backend, runtime);
871
922
  const { file_path, offset = 0, limit = 100 } = input;
872
923
  let result = await resolvedBackend.read(file_path, offset, limit);
873
924
  const lines = result.split("\n");
@@ -893,17 +944,14 @@ function createReadFileTool(backend, options) {
893
944
  */
894
945
  function createWriteFileTool(backend, options) {
895
946
  const { customDescription } = options;
896
- return (0, langchain.tool)(async (input, config) => {
897
- const resolvedBackend = getBackend(backend, {
898
- state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
899
- store: config.store
900
- });
947
+ return (0, langchain.tool)(async (input, runtime) => {
948
+ const resolvedBackend = await resolveBackend(backend, runtime);
901
949
  const { file_path, content } = input;
902
950
  const result = await resolvedBackend.write(file_path, content);
903
951
  if (result.error) return result.error;
904
952
  const message = new langchain.ToolMessage({
905
953
  content: `Successfully wrote to '${file_path}'`,
906
- tool_call_id: config.toolCall?.id,
954
+ tool_call_id: runtime.toolCall?.id,
907
955
  name: "write_file",
908
956
  metadata: result.metadata
909
957
  });
@@ -926,17 +974,14 @@ function createWriteFileTool(backend, options) {
926
974
  */
927
975
  function createEditFileTool(backend, options) {
928
976
  const { customDescription } = options;
929
- return (0, langchain.tool)(async (input, config) => {
930
- const resolvedBackend = getBackend(backend, {
931
- state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
932
- store: config.store
933
- });
977
+ return (0, langchain.tool)(async (input, runtime) => {
978
+ const resolvedBackend = await resolveBackend(backend, runtime);
934
979
  const { file_path, old_string, new_string, replace_all = false } = input;
935
980
  const result = await resolvedBackend.edit(file_path, old_string, new_string, replace_all);
936
981
  if (result.error) return result.error;
937
982
  const message = new langchain.ToolMessage({
938
983
  content: `Successfully replaced ${result.occurrences} occurrence(s) in '${file_path}'`,
939
- tool_call_id: config.toolCall?.id,
984
+ tool_call_id: runtime.toolCall?.id,
940
985
  name: "edit_file",
941
986
  metadata: result.metadata
942
987
  });
@@ -961,11 +1006,8 @@ function createEditFileTool(backend, options) {
961
1006
  */
962
1007
  function createGlobTool(backend, options) {
963
1008
  const { customDescription } = options;
964
- return (0, langchain.tool)(async (input, config) => {
965
- const resolvedBackend = getBackend(backend, {
966
- state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
967
- store: config.store
968
- });
1009
+ return (0, langchain.tool)(async (input, runtime) => {
1010
+ const resolvedBackend = await resolveBackend(backend, runtime);
969
1011
  const { pattern, path = "/" } = input;
970
1012
  const infos = await resolvedBackend.globInfo(pattern, path);
971
1013
  if (infos.length === 0) return `No files found matching pattern '${pattern}'`;
@@ -986,11 +1028,8 @@ function createGlobTool(backend, options) {
986
1028
  */
987
1029
  function createGrepTool(backend, options) {
988
1030
  const { customDescription } = options;
989
- return (0, langchain.tool)(async (input, config) => {
990
- const resolvedBackend = getBackend(backend, {
991
- state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
992
- store: config.store
993
- });
1031
+ return (0, langchain.tool)(async (input, runtime) => {
1032
+ const resolvedBackend = await resolveBackend(backend, runtime);
994
1033
  const { pattern, path = "/", glob = null } = input;
995
1034
  const result = await resolvedBackend.grepRaw(pattern, path, glob);
996
1035
  if (typeof result === "string") return result;
@@ -1022,11 +1061,8 @@ function createGrepTool(backend, options) {
1022
1061
  */
1023
1062
  function createExecuteTool(backend, options) {
1024
1063
  const { customDescription } = options;
1025
- return (0, langchain.tool)(async (input, config) => {
1026
- const resolvedBackend = getBackend(backend, {
1027
- state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
1028
- store: config.store
1029
- });
1064
+ return (0, langchain.tool)(async (input, runtime) => {
1065
+ const resolvedBackend = await resolveBackend(backend, runtime);
1030
1066
  if (!isSandboxBackend(resolvedBackend)) return "Error: Execution not available. This agent's backend does not support command execution (SandboxBackendProtocol). To use the execute tool, provide a backend that implements SandboxBackendProtocol.";
1031
1067
  const result = await resolvedBackend.execute(input.command);
1032
1068
  const parts = [result.output];
@@ -1046,7 +1082,7 @@ function createExecuteTool(backend, options) {
1046
1082
  * Create filesystem middleware with all tools and features.
1047
1083
  */
1048
1084
  function createFilesystemMiddleware(options = {}) {
1049
- const { backend = (stateAndStore) => new StateBackend(stateAndStore), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4 } = options;
1085
+ const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, humanMessageTokenLimitBeforeEvict = 5e4 } = options;
1050
1086
  const baseSystemPrompt = customSystemPrompt || FILESYSTEM_SYSTEM_PROMPT;
1051
1087
  const allToolsByName = {
1052
1088
  ls: createLsTool(backend, { customDescription: customToolDescriptions?.ls }),
@@ -1064,19 +1100,53 @@ function createFilesystemMiddleware(options = {}) {
1064
1100
  name: "FilesystemMiddleware",
1065
1101
  stateSchema: FilesystemStateSchema,
1066
1102
  tools: Object.values(allToolsByName),
1103
+ async beforeAgent(state) {
1104
+ if (!humanMessageTokenLimitBeforeEvict) return;
1105
+ const messages = state.messages;
1106
+ if (!messages || messages.length === 0) return;
1107
+ const last = messages[messages.length - 1];
1108
+ if (!langchain.HumanMessage.isInstance(last)) return;
1109
+ if (last.additional_kwargs?.lc_evicted_to) return;
1110
+ const contentStr = extractTextFromMessage(last);
1111
+ const threshold = 4 * humanMessageTokenLimitBeforeEvict;
1112
+ if (contentStr.length <= threshold) return;
1113
+ const resolvedBackend = await resolveBackend(backend, { state: state || {} });
1114
+ const filePath = `/conversation_history/${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
1115
+ const writeResult = await resolvedBackend.write(filePath, contentStr);
1116
+ if (writeResult.error) return;
1117
+ const result = { messages: [new langchain.HumanMessage({
1118
+ content: last.content,
1119
+ id: last.id,
1120
+ additional_kwargs: {
1121
+ ...last.additional_kwargs,
1122
+ lc_evicted_to: filePath
1123
+ },
1124
+ response_metadata: { ...last.response_metadata }
1125
+ })] };
1126
+ if (writeResult.filesUpdate) result.files = writeResult.filesUpdate;
1127
+ return result;
1128
+ },
1067
1129
  wrapModelCall: async (request, handler) => {
1068
- const supportsExecution = isSandboxBackend(getBackend(backend, {
1069
- state: request.state || {},
1070
- store: request.runtime?.store
1130
+ const supportsExecution = isSandboxBackend(await resolveBackend(backend, {
1131
+ ...request.runtime,
1132
+ state: request.state
1071
1133
  }));
1072
1134
  let tools = request.tools;
1073
1135
  if (!supportsExecution) tools = tools.filter((t) => t.name !== "execute");
1074
1136
  let filesystemPrompt = baseSystemPrompt;
1075
1137
  if (supportsExecution) filesystemPrompt = `${filesystemPrompt}\n\n${EXECUTION_SYSTEM_PROMPT}`;
1076
1138
  const newSystemMessage = request.systemMessage.concat(filesystemPrompt);
1139
+ let messages = request.messages;
1140
+ if (humanMessageTokenLimitBeforeEvict && messages) {
1141
+ if (messages.some((msg) => langchain.HumanMessage.isInstance(msg) && msg.additional_kwargs?.lc_evicted_to)) messages = messages.map((msg) => {
1142
+ if (langchain.HumanMessage.isInstance(msg) && msg.additional_kwargs?.lc_evicted_to) return buildTruncatedHumanMessage(msg, msg.additional_kwargs.lc_evicted_to);
1143
+ return msg;
1144
+ });
1145
+ }
1077
1146
  return handler({
1078
1147
  ...request,
1079
1148
  tools,
1149
+ messages,
1080
1150
  systemMessage: newSystemMessage
1081
1151
  });
1082
1152
  },
@@ -1087,9 +1157,9 @@ function createFilesystemMiddleware(options = {}) {
1087
1157
  const result = await handler(request);
1088
1158
  async function processToolMessage(msg, toolTokenLimitBeforeEvict) {
1089
1159
  if (typeof msg.content === "string" && msg.content.length > toolTokenLimitBeforeEvict * 4) {
1090
- const resolvedBackend = getBackend(backend, {
1091
- state: request.state || {},
1092
- store: request.runtime?.store
1160
+ const resolvedBackend = await resolveBackend(backend, {
1161
+ ...request.runtime,
1162
+ state: request.state
1093
1163
  });
1094
1164
  const evictPath = `/large_tool_results/${sanitizeToolCallId(request.toolCall?.id || msg.tool_call_id)}`;
1095
1165
  const writeResult = await resolvedBackend.write(evictPath, msg.content);
@@ -1857,19 +1927,12 @@ async function loadMemoryFromBackend(backend, path) {
1857
1927
  */
1858
1928
  function createMemoryMiddleware(options) {
1859
1929
  const { backend, sources, addCacheControl = false } = options;
1860
- /**
1861
- * Resolve backend from instance or factory.
1862
- */
1863
- function getBackend(state) {
1864
- if (typeof backend === "function") return backend({ state });
1865
- return backend;
1866
- }
1867
1930
  return (0, langchain.createMiddleware)({
1868
1931
  name: "MemoryMiddleware",
1869
1932
  stateSchema: MemoryStateSchema,
1870
1933
  async beforeAgent(state) {
1871
1934
  if ("memoryContents" in state && state.memoryContents != null) return;
1872
- const resolvedBackend = getBackend(state);
1935
+ const resolvedBackend = await resolveBackend(backend, { state });
1873
1936
  const contents = {};
1874
1937
  for (const path of sources) try {
1875
1938
  const content = await loadMemoryFromBackend(resolvedBackend, path);
@@ -2271,13 +2334,6 @@ function formatSkillsList(skills, sources) {
2271
2334
  function createSkillsMiddleware(options) {
2272
2335
  const { backend, sources } = options;
2273
2336
  let loadedSkills = [];
2274
- /**
2275
- * Resolve backend from instance or factory.
2276
- */
2277
- function getBackend(state) {
2278
- if (typeof backend === "function") return backend({ state });
2279
- return backend;
2280
- }
2281
2337
  return (0, langchain.createMiddleware)({
2282
2338
  name: "SkillsMiddleware",
2283
2339
  stateSchema: SkillsStateSchema,
@@ -2287,7 +2343,7 @@ function createSkillsMiddleware(options) {
2287
2343
  loadedSkills = state.skillsMetadata;
2288
2344
  return;
2289
2345
  }
2290
- const resolvedBackend = getBackend(state);
2346
+ const resolvedBackend = await resolveBackend(backend, { state });
2291
2347
  const allSkills = /* @__PURE__ */ new Map();
2292
2348
  for (const sourcePath of sources) try {
2293
2349
  const skills = await listSkillsFromBackend(resolvedBackend, sourcePath);
@@ -2511,13 +2567,6 @@ function createSummarizationMiddleware(options) {
2511
2567
  let sessionId = null;
2512
2568
  let tokenEstimationMultiplier = 1;
2513
2569
  /**
2514
- * Resolve backend from instance or factory.
2515
- */
2516
- function getBackend(state) {
2517
- if (typeof backend === "function") return backend({ state });
2518
- return backend;
2519
- }
2520
- /**
2521
2570
  * Get or create session ID for history file naming.
2522
2571
  */
2523
2572
  function getSessionId(state) {
@@ -2883,7 +2932,7 @@ ${summary}
2883
2932
  * the file path, and the state cutoff index.
2884
2933
  */
2885
2934
  async function summarizeMessages(messagesToSummarize, resolvedModel, state, previousCutoffIndex, cutoffIndex) {
2886
- const filePath = await offloadToBackend(getBackend(state), messagesToSummarize, state);
2935
+ const filePath = await offloadToBackend(await resolveBackend(backend, { state }), messagesToSummarize, state);
2887
2936
  if (filePath === null) console.warn(`[SummarizationMiddleware] Backend offload failed during summarization. Proceeding with summary generation.`);
2888
2937
  return {
2889
2938
  summaryMessage: buildSummaryMessage(await createSummary(messagesToSummarize, resolvedModel), filePath),
@@ -4964,9 +5013,9 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
4964
5013
  * ```
4965
5014
  */
4966
5015
  static async create(options = {}) {
4967
- const { templateName = "deepagents", apiKey = process.env.LANGSMITH_API_KEY, defaultTimeout } = options;
5016
+ const { templateName = "deepagents", apiKey = process.env.LANGSMITH_API_KEY, defaultTimeout, ...createSandboxOptions } = options;
4968
5017
  return new LangSmithSandbox({
4969
- sandbox: await new langsmith_experimental_sandbox.SandboxClient({ apiKey }).createSandbox(templateName),
5018
+ sandbox: await new langsmith_experimental_sandbox.SandboxClient({ apiKey }).createSandbox(templateName, createSandboxOptions),
4970
5019
  defaultTimeout
4971
5020
  });
4972
5021
  }
@@ -5125,7 +5174,7 @@ function createDeepAgent(params = {}) {
5125
5174
  * Create backend configuration for filesystem middleware
5126
5175
  * If no backend is provided, use a factory that creates a StateBackend
5127
5176
  */
5128
- const filesystemBackend = backend ? backend : (config) => new StateBackend(config);
5177
+ const filesystemBackend = backend ? backend : (runtime) => new StateBackend(runtime);
5129
5178
  /**
5130
5179
  * Skills middleware (created conditionally for runtime use)
5131
5180
  */
@@ -5793,5 +5842,6 @@ exports.findProjectRoot = findProjectRoot;
5793
5842
  exports.isSandboxBackend = isSandboxBackend;
5794
5843
  exports.listSkills = listSkills;
5795
5844
  exports.parseSkillMetadata = parseSkillMetadata;
5845
+ exports.resolveBackend = resolveBackend;
5796
5846
 
5797
5847
  //# sourceMappingURL=index.cjs.map