deepagents 1.8.4 → 1.8.6
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 +352 -217
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +176 -39
- package/dist/index.d.ts +176 -39
- package/dist/index.js +343 -205
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value:
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -7,16 +7,12 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
9
|
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
20
16
|
}
|
|
21
17
|
return to;
|
|
22
18
|
};
|
|
@@ -24,7 +20,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
20
|
value: mod,
|
|
25
21
|
enumerable: true
|
|
26
22
|
}) : target, mod));
|
|
27
|
-
|
|
28
23
|
//#endregion
|
|
29
24
|
let langchain = require("langchain");
|
|
30
25
|
let _langchain_core_runnables = require("@langchain/core/runnables");
|
|
@@ -37,7 +32,6 @@ let _langchain_core_messages = require("@langchain/core/messages");
|
|
|
37
32
|
let zod = require("zod");
|
|
38
33
|
let yaml = require("yaml");
|
|
39
34
|
yaml = __toESM(yaml);
|
|
40
|
-
let uuid = require("uuid");
|
|
41
35
|
let _langchain_core_errors = require("@langchain/core/errors");
|
|
42
36
|
let langchain_chat_models_universal = require("langchain/chat_models/universal");
|
|
43
37
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -50,9 +44,9 @@ let node_child_process = require("node:child_process");
|
|
|
50
44
|
node_child_process = __toESM(node_child_process);
|
|
51
45
|
let fast_glob = require("fast-glob");
|
|
52
46
|
fast_glob = __toESM(fast_glob);
|
|
47
|
+
let langsmith_experimental_sandbox = require("langsmith/experimental/sandbox");
|
|
53
48
|
let node_os = require("node:os");
|
|
54
49
|
node_os = __toESM(node_os);
|
|
55
|
-
|
|
56
50
|
//#region src/backends/protocol.ts
|
|
57
51
|
/**
|
|
58
52
|
* Type guard to check if a backend supports execution.
|
|
@@ -61,7 +55,7 @@ node_os = __toESM(node_os);
|
|
|
61
55
|
* @returns True if the backend implements SandboxBackendProtocol
|
|
62
56
|
*/
|
|
63
57
|
function isSandboxBackend(backend) {
|
|
64
|
-
return typeof backend.execute === "function" && typeof backend.id === "string";
|
|
58
|
+
return typeof backend.execute === "function" && typeof backend.id === "string" && backend.id !== "";
|
|
65
59
|
}
|
|
66
60
|
const SANDBOX_ERROR_SYMBOL = Symbol.for("sandbox.error");
|
|
67
61
|
/**
|
|
@@ -112,7 +106,17 @@ var SandboxError = class SandboxError extends Error {
|
|
|
112
106
|
return typeof error === "object" && error !== null && error[SANDBOX_ERROR_SYMBOL] === true;
|
|
113
107
|
}
|
|
114
108
|
};
|
|
115
|
-
|
|
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
|
+
}
|
|
116
120
|
//#endregion
|
|
117
121
|
//#region src/backends/utils.ts
|
|
118
122
|
/**
|
|
@@ -124,7 +128,6 @@ var SandboxError = class SandboxError extends Error {
|
|
|
124
128
|
*/
|
|
125
129
|
const EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents";
|
|
126
130
|
const MAX_LINE_LENGTH = 1e4;
|
|
127
|
-
const LINE_NUMBER_WIDTH = 6;
|
|
128
131
|
const TOOL_RESULT_TOKEN_LIMIT = 2e4;
|
|
129
132
|
const TRUNCATION_GUIDANCE = "... [results truncated, try being more specific with your parameters]";
|
|
130
133
|
/**
|
|
@@ -154,17 +157,17 @@ function formatContentWithLineNumbers(content, startLine = 1) {
|
|
|
154
157
|
for (let i = 0; i < lines.length; i++) {
|
|
155
158
|
const line = lines[i];
|
|
156
159
|
const lineNum = i + startLine;
|
|
157
|
-
if (line.length <=
|
|
160
|
+
if (line.length <= 1e4) resultLines.push(`${lineNum.toString().padStart(6)}\t${line}`);
|
|
158
161
|
else {
|
|
159
162
|
const numChunks = Math.ceil(line.length / MAX_LINE_LENGTH);
|
|
160
163
|
for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
|
|
161
164
|
const start = chunkIdx * MAX_LINE_LENGTH;
|
|
162
165
|
const end = Math.min(start + MAX_LINE_LENGTH, line.length);
|
|
163
166
|
const chunk = line.substring(start, end);
|
|
164
|
-
if (chunkIdx === 0) resultLines.push(`${lineNum.toString().padStart(
|
|
167
|
+
if (chunkIdx === 0) resultLines.push(`${lineNum.toString().padStart(6)}\t${chunk}`);
|
|
165
168
|
else {
|
|
166
169
|
const continuationMarker = `${lineNum}.${chunkIdx}`;
|
|
167
|
-
resultLines.push(`${continuationMarker.padStart(
|
|
170
|
+
resultLines.push(`${continuationMarker.padStart(6)}\t${chunk}`);
|
|
168
171
|
}
|
|
169
172
|
}
|
|
170
173
|
}
|
|
@@ -267,13 +270,13 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
|
|
|
267
270
|
function truncateIfTooLong(result) {
|
|
268
271
|
if (Array.isArray(result)) {
|
|
269
272
|
const totalChars = result.reduce((sum, item) => sum + item.length, 0);
|
|
270
|
-
if (totalChars >
|
|
273
|
+
if (totalChars > 2e4 * 4) {
|
|
271
274
|
const truncateAt = Math.floor(result.length * TOOL_RESULT_TOKEN_LIMIT * 4 / totalChars);
|
|
272
275
|
return [...result.slice(0, truncateAt), TRUNCATION_GUIDANCE];
|
|
273
276
|
}
|
|
274
277
|
return result;
|
|
275
278
|
}
|
|
276
|
-
if (result.length >
|
|
279
|
+
if (result.length > 2e4 * 4) return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n... [results truncated, try being more specific with your parameters]";
|
|
277
280
|
return result;
|
|
278
281
|
}
|
|
279
282
|
/**
|
|
@@ -381,7 +384,6 @@ function grepMatchesFromFiles(files, pattern, path$9 = null, glob = null) {
|
|
|
381
384
|
}
|
|
382
385
|
return matches;
|
|
383
386
|
}
|
|
384
|
-
|
|
385
387
|
//#endregion
|
|
386
388
|
//#region src/backends/state.ts
|
|
387
389
|
/**
|
|
@@ -396,15 +398,15 @@ function grepMatchesFromFiles(files, pattern, path$9 = null, glob = null) {
|
|
|
396
398
|
* for the middleware to apply via Command.
|
|
397
399
|
*/
|
|
398
400
|
var StateBackend = class {
|
|
399
|
-
|
|
400
|
-
constructor(
|
|
401
|
-
this.
|
|
401
|
+
runtime;
|
|
402
|
+
constructor(runtime) {
|
|
403
|
+
this.runtime = runtime;
|
|
402
404
|
}
|
|
403
405
|
/**
|
|
404
406
|
* Get files from current state.
|
|
405
407
|
*/
|
|
406
408
|
getFiles() {
|
|
407
|
-
return this.
|
|
409
|
+
return this.runtime.state.files || {};
|
|
408
410
|
}
|
|
409
411
|
/**
|
|
410
412
|
* List files and directories in the specified directory (non-recursive).
|
|
@@ -581,7 +583,6 @@ var StateBackend = class {
|
|
|
581
583
|
return responses;
|
|
582
584
|
}
|
|
583
585
|
};
|
|
584
|
-
|
|
585
586
|
//#endregion
|
|
586
587
|
//#region src/middleware/fs.ts
|
|
587
588
|
/**
|
|
@@ -614,6 +615,20 @@ var StateBackend = class {
|
|
|
614
615
|
* These tools return minimal confirmation messages and are never expected to produce
|
|
615
616
|
* output large enough to exceed token limits, so checking them would be unnecessary.
|
|
616
617
|
*/
|
|
618
|
+
/**
|
|
619
|
+
* All tool names registered by FilesystemMiddleware.
|
|
620
|
+
* This is the single source of truth — used by createDeepAgent to detect
|
|
621
|
+
* collisions with user-supplied tools at construction time.
|
|
622
|
+
*/
|
|
623
|
+
const FILESYSTEM_TOOL_NAMES = [
|
|
624
|
+
"ls",
|
|
625
|
+
"read_file",
|
|
626
|
+
"write_file",
|
|
627
|
+
"edit_file",
|
|
628
|
+
"glob",
|
|
629
|
+
"grep",
|
|
630
|
+
"execute"
|
|
631
|
+
];
|
|
617
632
|
const TOOLS_EXCLUDED_FROM_EVICTION = [
|
|
618
633
|
"ls",
|
|
619
634
|
"glob",
|
|
@@ -623,17 +638,6 @@ const TOOLS_EXCLUDED_FROM_EVICTION = [
|
|
|
623
638
|
"write_file"
|
|
624
639
|
];
|
|
625
640
|
/**
|
|
626
|
-
* Approximate number of characters per token for truncation calculations.
|
|
627
|
-
* Using 4 chars per token as a conservative approximation (actual ratio varies by content)
|
|
628
|
-
* This errs on the high side to avoid premature eviction of content that might fit.
|
|
629
|
-
*/
|
|
630
|
-
const NUM_CHARS_PER_TOKEN = 4;
|
|
631
|
-
/**
|
|
632
|
-
* Default values for read_file tool pagination (in lines).
|
|
633
|
-
*/
|
|
634
|
-
const DEFAULT_READ_LINE_OFFSET = 0;
|
|
635
|
-
const DEFAULT_READ_LINE_LIMIT = 100;
|
|
636
|
-
/**
|
|
637
641
|
* Template for truncation message in read_file.
|
|
638
642
|
* {file_path} will be filled in at runtime.
|
|
639
643
|
*/
|
|
@@ -715,16 +719,6 @@ const FilesystemStateSchema = new _langchain_langgraph.StateSchema({ files: new
|
|
|
715
719
|
inputSchema: zod_v4.z.record(zod_v4.z.string(), FileDataSchema.nullable()).optional(),
|
|
716
720
|
reducer: fileDataReducer
|
|
717
721
|
}) });
|
|
718
|
-
/**
|
|
719
|
-
* Resolve backend from factory or instance.
|
|
720
|
-
*
|
|
721
|
-
* @param backend - Backend instance or factory function
|
|
722
|
-
* @param stateAndStore - State and store container for backend initialization
|
|
723
|
-
*/
|
|
724
|
-
function getBackend(backend, stateAndStore) {
|
|
725
|
-
if (typeof backend === "function") return backend(stateAndStore);
|
|
726
|
-
return backend;
|
|
727
|
-
}
|
|
728
722
|
const FILESYSTEM_SYSTEM_PROMPT = `## Filesystem Tools \`ls\`, \`read_file\`, \`write_file\`, \`edit_file\`, \`glob\`, \`grep\`
|
|
729
723
|
|
|
730
724
|
You have access to a filesystem which you can interact with using these tools.
|
|
@@ -842,11 +836,8 @@ Use this tool to run commands, scripts, tests, builds, and other shell operation
|
|
|
842
836
|
*/
|
|
843
837
|
function createLsTool(backend, options) {
|
|
844
838
|
const { customDescription } = options;
|
|
845
|
-
return (0, langchain.tool)(async (input,
|
|
846
|
-
const resolvedBackend =
|
|
847
|
-
state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
|
|
848
|
-
store: config.store
|
|
849
|
-
});
|
|
839
|
+
return (0, langchain.tool)(async (input, runtime) => {
|
|
840
|
+
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
850
841
|
const path = input.path || "/";
|
|
851
842
|
const infos = await resolvedBackend.lsInfo(path);
|
|
852
843
|
if (infos.length === 0) return `No files found in ${path}`;
|
|
@@ -870,18 +861,15 @@ function createLsTool(backend, options) {
|
|
|
870
861
|
*/
|
|
871
862
|
function createReadFileTool(backend, options) {
|
|
872
863
|
const { customDescription, toolTokenLimitBeforeEvict } = options;
|
|
873
|
-
return (0, langchain.tool)(async (input,
|
|
874
|
-
const resolvedBackend =
|
|
875
|
-
|
|
876
|
-
store: config.store
|
|
877
|
-
});
|
|
878
|
-
const { file_path, offset = DEFAULT_READ_LINE_OFFSET, limit = DEFAULT_READ_LINE_LIMIT } = input;
|
|
864
|
+
return (0, langchain.tool)(async (input, runtime) => {
|
|
865
|
+
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
866
|
+
const { file_path, offset = 0, limit = 100 } = input;
|
|
879
867
|
let result = await resolvedBackend.read(file_path, offset, limit);
|
|
880
868
|
const lines = result.split("\n");
|
|
881
869
|
if (lines.length > limit) result = lines.slice(0, limit).join("\n");
|
|
882
|
-
if (toolTokenLimitBeforeEvict && result.length >=
|
|
870
|
+
if (toolTokenLimitBeforeEvict && result.length >= 4 * toolTokenLimitBeforeEvict) {
|
|
883
871
|
const truncationMsg = READ_FILE_TRUNCATION_MSG.replace("{file_path}", file_path);
|
|
884
|
-
const maxContentLength =
|
|
872
|
+
const maxContentLength = 4 * toolTokenLimitBeforeEvict - truncationMsg.length;
|
|
885
873
|
result = result.substring(0, maxContentLength) + truncationMsg;
|
|
886
874
|
}
|
|
887
875
|
return result;
|
|
@@ -890,8 +878,8 @@ function createReadFileTool(backend, options) {
|
|
|
890
878
|
description: customDescription || READ_FILE_TOOL_DESCRIPTION,
|
|
891
879
|
schema: zod_v4.z.object({
|
|
892
880
|
file_path: zod_v4.z.string().describe("Absolute path to the file to read"),
|
|
893
|
-
offset: zod_v4.z.coerce.number().optional().default(
|
|
894
|
-
limit: zod_v4.z.coerce.number().optional().default(
|
|
881
|
+
offset: zod_v4.z.coerce.number().optional().default(0).describe("Line offset to start reading from (0-indexed)"),
|
|
882
|
+
limit: zod_v4.z.coerce.number().optional().default(100).describe("Maximum number of lines to read")
|
|
895
883
|
})
|
|
896
884
|
});
|
|
897
885
|
}
|
|
@@ -900,17 +888,14 @@ function createReadFileTool(backend, options) {
|
|
|
900
888
|
*/
|
|
901
889
|
function createWriteFileTool(backend, options) {
|
|
902
890
|
const { customDescription } = options;
|
|
903
|
-
return (0, langchain.tool)(async (input,
|
|
904
|
-
const resolvedBackend =
|
|
905
|
-
state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
|
|
906
|
-
store: config.store
|
|
907
|
-
});
|
|
891
|
+
return (0, langchain.tool)(async (input, runtime) => {
|
|
892
|
+
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
908
893
|
const { file_path, content } = input;
|
|
909
894
|
const result = await resolvedBackend.write(file_path, content);
|
|
910
895
|
if (result.error) return result.error;
|
|
911
896
|
const message = new langchain.ToolMessage({
|
|
912
897
|
content: `Successfully wrote to '${file_path}'`,
|
|
913
|
-
tool_call_id:
|
|
898
|
+
tool_call_id: runtime.toolCall?.id,
|
|
914
899
|
name: "write_file",
|
|
915
900
|
metadata: result.metadata
|
|
916
901
|
});
|
|
@@ -933,17 +918,14 @@ function createWriteFileTool(backend, options) {
|
|
|
933
918
|
*/
|
|
934
919
|
function createEditFileTool(backend, options) {
|
|
935
920
|
const { customDescription } = options;
|
|
936
|
-
return (0, langchain.tool)(async (input,
|
|
937
|
-
const resolvedBackend =
|
|
938
|
-
state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
|
|
939
|
-
store: config.store
|
|
940
|
-
});
|
|
921
|
+
return (0, langchain.tool)(async (input, runtime) => {
|
|
922
|
+
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
941
923
|
const { file_path, old_string, new_string, replace_all = false } = input;
|
|
942
924
|
const result = await resolvedBackend.edit(file_path, old_string, new_string, replace_all);
|
|
943
925
|
if (result.error) return result.error;
|
|
944
926
|
const message = new langchain.ToolMessage({
|
|
945
927
|
content: `Successfully replaced ${result.occurrences} occurrence(s) in '${file_path}'`,
|
|
946
|
-
tool_call_id:
|
|
928
|
+
tool_call_id: runtime.toolCall?.id,
|
|
947
929
|
name: "edit_file",
|
|
948
930
|
metadata: result.metadata
|
|
949
931
|
});
|
|
@@ -968,11 +950,8 @@ function createEditFileTool(backend, options) {
|
|
|
968
950
|
*/
|
|
969
951
|
function createGlobTool(backend, options) {
|
|
970
952
|
const { customDescription } = options;
|
|
971
|
-
return (0, langchain.tool)(async (input,
|
|
972
|
-
const resolvedBackend =
|
|
973
|
-
state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
|
|
974
|
-
store: config.store
|
|
975
|
-
});
|
|
953
|
+
return (0, langchain.tool)(async (input, runtime) => {
|
|
954
|
+
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
976
955
|
const { pattern, path = "/" } = input;
|
|
977
956
|
const infos = await resolvedBackend.globInfo(pattern, path);
|
|
978
957
|
if (infos.length === 0) return `No files found matching pattern '${pattern}'`;
|
|
@@ -993,11 +972,8 @@ function createGlobTool(backend, options) {
|
|
|
993
972
|
*/
|
|
994
973
|
function createGrepTool(backend, options) {
|
|
995
974
|
const { customDescription } = options;
|
|
996
|
-
return (0, langchain.tool)(async (input,
|
|
997
|
-
const resolvedBackend =
|
|
998
|
-
state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
|
|
999
|
-
store: config.store
|
|
1000
|
-
});
|
|
975
|
+
return (0, langchain.tool)(async (input, runtime) => {
|
|
976
|
+
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
1001
977
|
const { pattern, path = "/", glob = null } = input;
|
|
1002
978
|
const result = await resolvedBackend.grepRaw(pattern, path, glob);
|
|
1003
979
|
if (typeof result === "string") return result;
|
|
@@ -1029,11 +1005,8 @@ function createGrepTool(backend, options) {
|
|
|
1029
1005
|
*/
|
|
1030
1006
|
function createExecuteTool(backend, options) {
|
|
1031
1007
|
const { customDescription } = options;
|
|
1032
|
-
return (0, langchain.tool)(async (input,
|
|
1033
|
-
const resolvedBackend =
|
|
1034
|
-
state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
|
|
1035
|
-
store: config.store
|
|
1036
|
-
});
|
|
1008
|
+
return (0, langchain.tool)(async (input, runtime) => {
|
|
1009
|
+
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
1037
1010
|
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.";
|
|
1038
1011
|
const result = await resolvedBackend.execute(input.command);
|
|
1039
1012
|
const parts = [result.output];
|
|
@@ -1053,27 +1026,28 @@ function createExecuteTool(backend, options) {
|
|
|
1053
1026
|
* Create filesystem middleware with all tools and features.
|
|
1054
1027
|
*/
|
|
1055
1028
|
function createFilesystemMiddleware(options = {}) {
|
|
1056
|
-
const { backend = (
|
|
1029
|
+
const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4 } = options;
|
|
1057
1030
|
const baseSystemPrompt = customSystemPrompt || FILESYSTEM_SYSTEM_PROMPT;
|
|
1031
|
+
const allToolsByName = {
|
|
1032
|
+
ls: createLsTool(backend, { customDescription: customToolDescriptions?.ls }),
|
|
1033
|
+
read_file: createReadFileTool(backend, {
|
|
1034
|
+
customDescription: customToolDescriptions?.read_file,
|
|
1035
|
+
toolTokenLimitBeforeEvict
|
|
1036
|
+
}),
|
|
1037
|
+
write_file: createWriteFileTool(backend, { customDescription: customToolDescriptions?.write_file }),
|
|
1038
|
+
edit_file: createEditFileTool(backend, { customDescription: customToolDescriptions?.edit_file }),
|
|
1039
|
+
glob: createGlobTool(backend, { customDescription: customToolDescriptions?.glob }),
|
|
1040
|
+
grep: createGrepTool(backend, { customDescription: customToolDescriptions?.grep }),
|
|
1041
|
+
execute: createExecuteTool(backend, { customDescription: customToolDescriptions?.execute })
|
|
1042
|
+
};
|
|
1058
1043
|
return (0, langchain.createMiddleware)({
|
|
1059
1044
|
name: "FilesystemMiddleware",
|
|
1060
1045
|
stateSchema: FilesystemStateSchema,
|
|
1061
|
-
tools:
|
|
1062
|
-
createLsTool(backend, { customDescription: customToolDescriptions?.ls }),
|
|
1063
|
-
createReadFileTool(backend, {
|
|
1064
|
-
customDescription: customToolDescriptions?.read_file,
|
|
1065
|
-
toolTokenLimitBeforeEvict
|
|
1066
|
-
}),
|
|
1067
|
-
createWriteFileTool(backend, { customDescription: customToolDescriptions?.write_file }),
|
|
1068
|
-
createEditFileTool(backend, { customDescription: customToolDescriptions?.edit_file }),
|
|
1069
|
-
createGlobTool(backend, { customDescription: customToolDescriptions?.glob }),
|
|
1070
|
-
createGrepTool(backend, { customDescription: customToolDescriptions?.grep }),
|
|
1071
|
-
createExecuteTool(backend, { customDescription: customToolDescriptions?.execute })
|
|
1072
|
-
],
|
|
1046
|
+
tools: Object.values(allToolsByName),
|
|
1073
1047
|
wrapModelCall: async (request, handler) => {
|
|
1074
|
-
const supportsExecution = isSandboxBackend(
|
|
1075
|
-
|
|
1076
|
-
|
|
1048
|
+
const supportsExecution = isSandboxBackend(await resolveBackend(backend, {
|
|
1049
|
+
...request.runtime,
|
|
1050
|
+
state: request.state
|
|
1077
1051
|
}));
|
|
1078
1052
|
let tools = request.tools;
|
|
1079
1053
|
if (!supportsExecution) tools = tools.filter((t) => t.name !== "execute");
|
|
@@ -1092,10 +1066,10 @@ function createFilesystemMiddleware(options = {}) {
|
|
|
1092
1066
|
if (toolName && TOOLS_EXCLUDED_FROM_EVICTION.includes(toolName)) return handler(request);
|
|
1093
1067
|
const result = await handler(request);
|
|
1094
1068
|
async function processToolMessage(msg, toolTokenLimitBeforeEvict) {
|
|
1095
|
-
if (typeof msg.content === "string" && msg.content.length > toolTokenLimitBeforeEvict *
|
|
1096
|
-
const resolvedBackend =
|
|
1097
|
-
|
|
1098
|
-
|
|
1069
|
+
if (typeof msg.content === "string" && msg.content.length > toolTokenLimitBeforeEvict * 4) {
|
|
1070
|
+
const resolvedBackend = await resolveBackend(backend, {
|
|
1071
|
+
...request.runtime,
|
|
1072
|
+
state: request.state
|
|
1099
1073
|
});
|
|
1100
1074
|
const evictPath = `/large_tool_results/${sanitizeToolCallId(request.toolCall?.id || msg.tool_call_id)}`;
|
|
1101
1075
|
const writeResult = await resolvedBackend.write(evictPath, msg.content);
|
|
@@ -1156,7 +1130,6 @@ function createFilesystemMiddleware(options = {}) {
|
|
|
1156
1130
|
}
|
|
1157
1131
|
});
|
|
1158
1132
|
}
|
|
1159
|
-
|
|
1160
1133
|
//#endregion
|
|
1161
1134
|
//#region src/middleware/subagents.ts
|
|
1162
1135
|
/**
|
|
@@ -1537,12 +1510,23 @@ function createSubAgentMiddleware(options) {
|
|
|
1537
1510
|
}
|
|
1538
1511
|
});
|
|
1539
1512
|
}
|
|
1540
|
-
|
|
1541
1513
|
//#endregion
|
|
1542
1514
|
//#region src/middleware/patch_tool_calls.ts
|
|
1543
1515
|
/**
|
|
1544
|
-
* Patch
|
|
1545
|
-
*
|
|
1516
|
+
* Patch tool call / tool response parity in a messages array.
|
|
1517
|
+
*
|
|
1518
|
+
* Ensures strict 1:1 correspondence between AIMessage tool_calls and
|
|
1519
|
+
* ToolMessage responses:
|
|
1520
|
+
*
|
|
1521
|
+
* 1. **Dangling tool_calls** — an AIMessage contains a tool_call with no
|
|
1522
|
+
* matching ToolMessage anywhere after it. A synthetic cancellation
|
|
1523
|
+
* ToolMessage is inserted immediately after the AIMessage.
|
|
1524
|
+
*
|
|
1525
|
+
* 2. **Orphaned ToolMessages** — a ToolMessage whose `tool_call_id` does not
|
|
1526
|
+
* match any tool_call in a preceding AIMessage. The ToolMessage is removed.
|
|
1527
|
+
*
|
|
1528
|
+
* Both directions are required for providers that enforce strict parity
|
|
1529
|
+
* (e.g. Google Gemini returns 400 INVALID_ARGUMENT otherwise).
|
|
1546
1530
|
*
|
|
1547
1531
|
* @param messages - The messages array to patch
|
|
1548
1532
|
* @returns Object with patched messages and needsPatch flag
|
|
@@ -1552,13 +1536,23 @@ function patchDanglingToolCalls(messages) {
|
|
|
1552
1536
|
patchedMessages: [],
|
|
1553
1537
|
needsPatch: false
|
|
1554
1538
|
};
|
|
1539
|
+
const allToolCallIds = /* @__PURE__ */ new Set();
|
|
1540
|
+
for (const msg of messages) if (langchain.AIMessage.isInstance(msg) && msg.tool_calls != null) {
|
|
1541
|
+
for (const tc of msg.tool_calls) if (tc.id) allToolCallIds.add(tc.id);
|
|
1542
|
+
}
|
|
1555
1543
|
const patchedMessages = [];
|
|
1556
1544
|
let needsPatch = false;
|
|
1557
1545
|
for (let i = 0; i < messages.length; i++) {
|
|
1558
1546
|
const msg = messages[i];
|
|
1547
|
+
if (langchain.ToolMessage.isInstance(msg)) {
|
|
1548
|
+
if (!allToolCallIds.has(msg.tool_call_id)) {
|
|
1549
|
+
needsPatch = true;
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1559
1553
|
patchedMessages.push(msg);
|
|
1560
1554
|
if (langchain.AIMessage.isInstance(msg) && msg.tool_calls != null) {
|
|
1561
|
-
for (const toolCall of msg.tool_calls) if (!messages.slice(i).find((m) => langchain.ToolMessage.isInstance(m) && m.tool_call_id === toolCall.id)) {
|
|
1555
|
+
for (const toolCall of msg.tool_calls) if (!messages.slice(i + 1).find((m) => langchain.ToolMessage.isInstance(m) && m.tool_call_id === toolCall.id)) {
|
|
1562
1556
|
needsPatch = true;
|
|
1563
1557
|
const toolMsg = `Tool call ${toolCall.name} with id ${toolCall.id} was cancelled - another message came in before it could be completed.`;
|
|
1564
1558
|
patchedMessages.push(new langchain.ToolMessage({
|
|
@@ -1575,11 +1569,18 @@ function patchDanglingToolCalls(messages) {
|
|
|
1575
1569
|
};
|
|
1576
1570
|
}
|
|
1577
1571
|
/**
|
|
1578
|
-
* Create middleware that
|
|
1572
|
+
* Create middleware that enforces strict tool call / tool response parity in
|
|
1573
|
+
* the messages history.
|
|
1579
1574
|
*
|
|
1580
|
-
*
|
|
1581
|
-
*
|
|
1582
|
-
*
|
|
1575
|
+
* Two kinds of violations are repaired:
|
|
1576
|
+
* 1. **Dangling tool_calls** — an AIMessage contains tool_calls with no
|
|
1577
|
+
* matching ToolMessage responses. Synthetic cancellation ToolMessages are
|
|
1578
|
+
* injected so every tool_call has a response.
|
|
1579
|
+
* 2. **Orphaned ToolMessages** — a ToolMessage exists whose `tool_call_id`
|
|
1580
|
+
* does not match any tool_call in a preceding AIMessage. These are removed.
|
|
1581
|
+
*
|
|
1582
|
+
* This is critical for providers like Google Gemini that reject requests with
|
|
1583
|
+
* mismatched function call / function response counts (400 INVALID_ARGUMENT).
|
|
1583
1584
|
*
|
|
1584
1585
|
* This middleware patches in two places:
|
|
1585
1586
|
* 1. `beforeAgent`: Patches state at the start of the agent loop (handles most cases)
|
|
@@ -1587,7 +1588,7 @@ function patchDanglingToolCalls(messages) {
|
|
|
1587
1588
|
* edge cases like HITL rejection during graph resume where state updates from
|
|
1588
1589
|
* beforeAgent may not be applied in time)
|
|
1589
1590
|
*
|
|
1590
|
-
* @returns AgentMiddleware that
|
|
1591
|
+
* @returns AgentMiddleware that enforces tool call / response parity
|
|
1591
1592
|
*
|
|
1592
1593
|
* @example
|
|
1593
1594
|
* ```typescript
|
|
@@ -1625,7 +1626,6 @@ function createPatchToolCallsMiddleware() {
|
|
|
1625
1626
|
}
|
|
1626
1627
|
});
|
|
1627
1628
|
}
|
|
1628
|
-
|
|
1629
1629
|
//#endregion
|
|
1630
1630
|
//#region src/values.ts
|
|
1631
1631
|
/**
|
|
@@ -1660,7 +1660,6 @@ const filesValue = new _langchain_langgraph.ReducedValue(zod.z.record(zod.z.stri
|
|
|
1660
1660
|
inputSchema: zod.z.record(zod.z.string(), FileDataSchema.nullable()).optional(),
|
|
1661
1661
|
reducer: fileDataReducer
|
|
1662
1662
|
});
|
|
1663
|
-
|
|
1664
1663
|
//#endregion
|
|
1665
1664
|
//#region src/middleware/memory.ts
|
|
1666
1665
|
/**
|
|
@@ -1838,19 +1837,12 @@ async function loadMemoryFromBackend(backend, path) {
|
|
|
1838
1837
|
*/
|
|
1839
1838
|
function createMemoryMiddleware(options) {
|
|
1840
1839
|
const { backend, sources, addCacheControl = false } = options;
|
|
1841
|
-
/**
|
|
1842
|
-
* Resolve backend from instance or factory.
|
|
1843
|
-
*/
|
|
1844
|
-
function getBackend(state) {
|
|
1845
|
-
if (typeof backend === "function") return backend({ state });
|
|
1846
|
-
return backend;
|
|
1847
|
-
}
|
|
1848
1840
|
return (0, langchain.createMiddleware)({
|
|
1849
1841
|
name: "MemoryMiddleware",
|
|
1850
1842
|
stateSchema: MemoryStateSchema,
|
|
1851
1843
|
async beforeAgent(state) {
|
|
1852
1844
|
if ("memoryContents" in state && state.memoryContents != null) return;
|
|
1853
|
-
const resolvedBackend =
|
|
1845
|
+
const resolvedBackend = await resolveBackend(backend, { state });
|
|
1854
1846
|
const contents = {};
|
|
1855
1847
|
for (const path of sources) try {
|
|
1856
1848
|
const content = await loadMemoryFromBackend(resolvedBackend, path);
|
|
@@ -1879,7 +1871,6 @@ function createMemoryMiddleware(options) {
|
|
|
1879
1871
|
}
|
|
1880
1872
|
});
|
|
1881
1873
|
}
|
|
1882
|
-
|
|
1883
1874
|
//#endregion
|
|
1884
1875
|
//#region src/middleware/skills.ts
|
|
1885
1876
|
/**
|
|
@@ -1925,7 +1916,6 @@ function createMemoryMiddleware(options) {
|
|
|
1925
1916
|
const MAX_SKILL_FILE_SIZE = 10 * 1024 * 1024;
|
|
1926
1917
|
const MAX_SKILL_NAME_LENGTH = 64;
|
|
1927
1918
|
const MAX_SKILL_DESCRIPTION_LENGTH = 1024;
|
|
1928
|
-
const MAX_SKILL_COMPATIBILITY_LENGTH = 500;
|
|
1929
1919
|
/**
|
|
1930
1920
|
* Zod schema for a single skill metadata entry.
|
|
1931
1921
|
*/
|
|
@@ -2035,7 +2025,7 @@ function validateSkillName$1(name, directoryName) {
|
|
|
2035
2025
|
valid: false,
|
|
2036
2026
|
error: "name is required"
|
|
2037
2027
|
};
|
|
2038
|
-
if (name.length >
|
|
2028
|
+
if (name.length > 64) return {
|
|
2039
2029
|
valid: false,
|
|
2040
2030
|
error: "name exceeds 64 characters"
|
|
2041
2031
|
};
|
|
@@ -2109,7 +2099,7 @@ function formatSkillAnnotations(skill) {
|
|
|
2109
2099
|
* validation errors occur
|
|
2110
2100
|
*/
|
|
2111
2101
|
function parseSkillMetadataFromContent(content, skillPath, directoryName) {
|
|
2112
|
-
if (content.length >
|
|
2102
|
+
if (content.length > 10485760) {
|
|
2113
2103
|
console.warn(`Skipping ${skillPath}: content too large (${content.length} bytes)`);
|
|
2114
2104
|
return null;
|
|
2115
2105
|
}
|
|
@@ -2139,7 +2129,7 @@ function parseSkillMetadataFromContent(content, skillPath, directoryName) {
|
|
|
2139
2129
|
const validation = validateSkillName$1(name, directoryName);
|
|
2140
2130
|
if (!validation.valid) console.warn(`Skill '${name}' in ${skillPath} does not follow Agent Skills specification: ${validation.error}. Consider renaming for spec compliance.`);
|
|
2141
2131
|
let descriptionStr = description;
|
|
2142
|
-
if (descriptionStr.length >
|
|
2132
|
+
if (descriptionStr.length > 1024) {
|
|
2143
2133
|
console.warn(`Description exceeds ${MAX_SKILL_DESCRIPTION_LENGTH} characters in ${skillPath}, truncating`);
|
|
2144
2134
|
descriptionStr = descriptionStr.slice(0, MAX_SKILL_DESCRIPTION_LENGTH);
|
|
2145
2135
|
}
|
|
@@ -2149,9 +2139,9 @@ function parseSkillMetadataFromContent(content, skillPath, directoryName) {
|
|
|
2149
2139
|
else allowedTools = String(rawTools).split(/\s+/).filter(Boolean);
|
|
2150
2140
|
else allowedTools = [];
|
|
2151
2141
|
let compatibilityStr = String(frontmatterData.compatibility ?? "").trim() || null;
|
|
2152
|
-
if (compatibilityStr && compatibilityStr.length >
|
|
2153
|
-
console.warn(`Compatibility exceeds
|
|
2154
|
-
compatibilityStr = compatibilityStr.slice(0,
|
|
2142
|
+
if (compatibilityStr && compatibilityStr.length > 500) {
|
|
2143
|
+
console.warn(`Compatibility exceeds 500 characters in ${skillPath}, truncating`);
|
|
2144
|
+
compatibilityStr = compatibilityStr.slice(0, 500);
|
|
2155
2145
|
}
|
|
2156
2146
|
return {
|
|
2157
2147
|
name,
|
|
@@ -2254,13 +2244,6 @@ function formatSkillsList(skills, sources) {
|
|
|
2254
2244
|
function createSkillsMiddleware(options) {
|
|
2255
2245
|
const { backend, sources } = options;
|
|
2256
2246
|
let loadedSkills = [];
|
|
2257
|
-
/**
|
|
2258
|
-
* Resolve backend from instance or factory.
|
|
2259
|
-
*/
|
|
2260
|
-
function getBackend(state) {
|
|
2261
|
-
if (typeof backend === "function") return backend({ state });
|
|
2262
|
-
return backend;
|
|
2263
|
-
}
|
|
2264
2247
|
return (0, langchain.createMiddleware)({
|
|
2265
2248
|
name: "SkillsMiddleware",
|
|
2266
2249
|
stateSchema: SkillsStateSchema,
|
|
@@ -2270,7 +2253,7 @@ function createSkillsMiddleware(options) {
|
|
|
2270
2253
|
loadedSkills = state.skillsMetadata;
|
|
2271
2254
|
return;
|
|
2272
2255
|
}
|
|
2273
|
-
const resolvedBackend =
|
|
2256
|
+
const resolvedBackend = await resolveBackend(backend, { state });
|
|
2274
2257
|
const allSkills = /* @__PURE__ */ new Map();
|
|
2275
2258
|
for (const sourcePath of sources) try {
|
|
2276
2259
|
const skills = await listSkillsFromBackend(resolvedBackend, sourcePath);
|
|
@@ -2294,7 +2277,6 @@ function createSkillsMiddleware(options) {
|
|
|
2294
2277
|
}
|
|
2295
2278
|
});
|
|
2296
2279
|
}
|
|
2297
|
-
|
|
2298
2280
|
//#endregion
|
|
2299
2281
|
//#region src/middleware/utils.ts
|
|
2300
2282
|
/**
|
|
@@ -2302,7 +2284,6 @@ function createSkillsMiddleware(options) {
|
|
|
2302
2284
|
*
|
|
2303
2285
|
* This module provides shared helpers used across middleware implementations.
|
|
2304
2286
|
*/
|
|
2305
|
-
|
|
2306
2287
|
//#endregion
|
|
2307
2288
|
//#region src/middleware/summarization.ts
|
|
2308
2289
|
/**
|
|
@@ -2496,18 +2477,11 @@ function createSummarizationMiddleware(options) {
|
|
|
2496
2477
|
let sessionId = null;
|
|
2497
2478
|
let tokenEstimationMultiplier = 1;
|
|
2498
2479
|
/**
|
|
2499
|
-
* Resolve backend from instance or factory.
|
|
2500
|
-
*/
|
|
2501
|
-
function getBackend(state) {
|
|
2502
|
-
if (typeof backend === "function") return backend({ state });
|
|
2503
|
-
return backend;
|
|
2504
|
-
}
|
|
2505
|
-
/**
|
|
2506
2480
|
* Get or create session ID for history file naming.
|
|
2507
2481
|
*/
|
|
2508
2482
|
function getSessionId(state) {
|
|
2509
2483
|
if (state._summarizationSessionId) return state._summarizationSessionId;
|
|
2510
|
-
if (!sessionId) sessionId = `session_${
|
|
2484
|
+
if (!sessionId) sessionId = `session_${crypto.randomUUID().substring(0, 8)}`;
|
|
2511
2485
|
return sessionId;
|
|
2512
2486
|
}
|
|
2513
2487
|
/**
|
|
@@ -2868,7 +2842,7 @@ ${summary}
|
|
|
2868
2842
|
* the file path, and the state cutoff index.
|
|
2869
2843
|
*/
|
|
2870
2844
|
async function summarizeMessages(messagesToSummarize, resolvedModel, state, previousCutoffIndex, cutoffIndex) {
|
|
2871
|
-
const filePath = await offloadToBackend(
|
|
2845
|
+
const filePath = await offloadToBackend(await resolveBackend(backend, { state }), messagesToSummarize, state);
|
|
2872
2846
|
if (filePath === null) console.warn(`[SummarizationMiddleware] Backend offload failed during summarization. Proceeding with summary generation.`);
|
|
2873
2847
|
return {
|
|
2874
2848
|
summaryMessage: buildSummaryMessage(await createSummary(messagesToSummarize, resolvedModel), filePath),
|
|
@@ -2992,7 +2966,6 @@ ${summary}
|
|
|
2992
2966
|
}
|
|
2993
2967
|
});
|
|
2994
2968
|
}
|
|
2995
|
-
|
|
2996
2969
|
//#endregion
|
|
2997
2970
|
//#region src/backends/store.ts
|
|
2998
2971
|
const NAMESPACE_COMPONENT_RE = /^[A-Za-z0-9\-_.@+:~]+$/;
|
|
@@ -3336,7 +3309,6 @@ var StoreBackend = class {
|
|
|
3336
3309
|
return responses;
|
|
3337
3310
|
}
|
|
3338
3311
|
};
|
|
3339
|
-
|
|
3340
3312
|
//#endregion
|
|
3341
3313
|
//#region src/backends/filesystem.ts
|
|
3342
3314
|
/**
|
|
@@ -3855,7 +3827,6 @@ var FilesystemBackend = class {
|
|
|
3855
3827
|
return responses;
|
|
3856
3828
|
}
|
|
3857
3829
|
};
|
|
3858
|
-
|
|
3859
3830
|
//#endregion
|
|
3860
3831
|
//#region src/backends/composite.ts
|
|
3861
3832
|
/**
|
|
@@ -4104,7 +4075,6 @@ var CompositeBackend = class {
|
|
|
4104
4075
|
return results;
|
|
4105
4076
|
}
|
|
4106
4077
|
};
|
|
4107
|
-
|
|
4108
4078
|
//#endregion
|
|
4109
4079
|
//#region src/backends/local-shell.ts
|
|
4110
4080
|
/**
|
|
@@ -4409,7 +4379,6 @@ var LocalShellBackend = class LocalShellBackend extends FilesystemBackend {
|
|
|
4409
4379
|
return backend;
|
|
4410
4380
|
}
|
|
4411
4381
|
};
|
|
4412
|
-
|
|
4413
4382
|
//#endregion
|
|
4414
4383
|
//#region src/backends/sandbox.ts
|
|
4415
4384
|
/**
|
|
@@ -4808,7 +4777,197 @@ var BaseSandbox = class {
|
|
|
4808
4777
|
};
|
|
4809
4778
|
}
|
|
4810
4779
|
};
|
|
4811
|
-
|
|
4780
|
+
//#endregion
|
|
4781
|
+
//#region src/backends/langsmith.ts
|
|
4782
|
+
/**
|
|
4783
|
+
* LangSmith Sandbox backend for deepagents.
|
|
4784
|
+
*
|
|
4785
|
+
* @example
|
|
4786
|
+
* ```typescript
|
|
4787
|
+
* import { LangSmithSandbox, createDeepAgent } from "deepagents";
|
|
4788
|
+
*
|
|
4789
|
+
* const sandbox = await LangSmithSandbox.create({ templateName: "deepagents-cli" });
|
|
4790
|
+
*
|
|
4791
|
+
* const agent = createDeepAgent({ model, backend: sandbox });
|
|
4792
|
+
*
|
|
4793
|
+
* try {
|
|
4794
|
+
* await agent.invoke({ messages: [...] });
|
|
4795
|
+
* } finally {
|
|
4796
|
+
* await sandbox.close();
|
|
4797
|
+
* }
|
|
4798
|
+
* ```
|
|
4799
|
+
*/
|
|
4800
|
+
/**
|
|
4801
|
+
* LangSmith Sandbox backend for deepagents.
|
|
4802
|
+
*
|
|
4803
|
+
* Extends `BaseSandbox` to provide command execution and file operations
|
|
4804
|
+
* via the LangSmith Sandbox API.
|
|
4805
|
+
*
|
|
4806
|
+
* Use the static `LangSmithSandbox.create()` factory for the simplest setup,
|
|
4807
|
+
* or construct directly with an existing `Sandbox` instance.
|
|
4808
|
+
*/
|
|
4809
|
+
var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
|
|
4810
|
+
#sandbox;
|
|
4811
|
+
#defaultTimeout;
|
|
4812
|
+
#isRunning = true;
|
|
4813
|
+
constructor(options) {
|
|
4814
|
+
super();
|
|
4815
|
+
this.#sandbox = options.sandbox;
|
|
4816
|
+
this.#defaultTimeout = options.defaultTimeout ?? 1800;
|
|
4817
|
+
}
|
|
4818
|
+
/** Whether the sandbox is currently active. */
|
|
4819
|
+
get isRunning() {
|
|
4820
|
+
return this.#isRunning;
|
|
4821
|
+
}
|
|
4822
|
+
/** Return the LangSmith sandbox name as the unique identifier. */
|
|
4823
|
+
get id() {
|
|
4824
|
+
return this.#sandbox.name;
|
|
4825
|
+
}
|
|
4826
|
+
/**
|
|
4827
|
+
* Execute a shell command in the LangSmith sandbox.
|
|
4828
|
+
*
|
|
4829
|
+
* @param command - Shell command string to execute
|
|
4830
|
+
* @param options.timeout - Override timeout in seconds; 0 disables timeout
|
|
4831
|
+
*/
|
|
4832
|
+
async execute(command, options) {
|
|
4833
|
+
const effectiveTimeout = options?.timeout !== void 0 ? options.timeout : this.#defaultTimeout;
|
|
4834
|
+
const result = await this.#sandbox.run(command, { timeout: effectiveTimeout });
|
|
4835
|
+
const out = result.stdout ?? "";
|
|
4836
|
+
return {
|
|
4837
|
+
output: result.stderr ? out ? `${out}\n${result.stderr}` : result.stderr : out,
|
|
4838
|
+
exitCode: result.exit_code,
|
|
4839
|
+
truncated: false
|
|
4840
|
+
};
|
|
4841
|
+
}
|
|
4842
|
+
/**
|
|
4843
|
+
* Download files from the sandbox using LangSmith's native file read API.
|
|
4844
|
+
* @param paths - List of file paths to download
|
|
4845
|
+
* @returns List of FileDownloadResponse objects, one per input path
|
|
4846
|
+
*/
|
|
4847
|
+
async downloadFiles(paths) {
|
|
4848
|
+
const responses = [];
|
|
4849
|
+
for (const path of paths) try {
|
|
4850
|
+
const content = await this.#sandbox.read(path);
|
|
4851
|
+
responses.push({
|
|
4852
|
+
path,
|
|
4853
|
+
content,
|
|
4854
|
+
error: null
|
|
4855
|
+
});
|
|
4856
|
+
} catch (err) {
|
|
4857
|
+
if (err instanceof langsmith_experimental_sandbox.LangSmithResourceNotFoundError) responses.push({
|
|
4858
|
+
path,
|
|
4859
|
+
content: null,
|
|
4860
|
+
error: "file_not_found"
|
|
4861
|
+
});
|
|
4862
|
+
else if (err instanceof langsmith_experimental_sandbox.LangSmithSandboxError) {
|
|
4863
|
+
const error = String(err.message).toLowerCase().includes("is a directory") ? "is_directory" : "file_not_found";
|
|
4864
|
+
responses.push({
|
|
4865
|
+
path,
|
|
4866
|
+
content: null,
|
|
4867
|
+
error
|
|
4868
|
+
});
|
|
4869
|
+
} else responses.push({
|
|
4870
|
+
path,
|
|
4871
|
+
content: null,
|
|
4872
|
+
error: "invalid_path"
|
|
4873
|
+
});
|
|
4874
|
+
}
|
|
4875
|
+
return responses;
|
|
4876
|
+
}
|
|
4877
|
+
/**
|
|
4878
|
+
* Upload files to the sandbox using LangSmith's native file write API.
|
|
4879
|
+
* @param files - List of [path, content] tuples to upload
|
|
4880
|
+
* @returns List of FileUploadResponse objects, one per input file
|
|
4881
|
+
*/
|
|
4882
|
+
async uploadFiles(files) {
|
|
4883
|
+
const responses = [];
|
|
4884
|
+
for (const [path, content] of files) try {
|
|
4885
|
+
await this.#sandbox.write(path, content);
|
|
4886
|
+
responses.push({
|
|
4887
|
+
path,
|
|
4888
|
+
error: null
|
|
4889
|
+
});
|
|
4890
|
+
} catch {
|
|
4891
|
+
responses.push({
|
|
4892
|
+
path,
|
|
4893
|
+
error: "permission_denied"
|
|
4894
|
+
});
|
|
4895
|
+
}
|
|
4896
|
+
return responses;
|
|
4897
|
+
}
|
|
4898
|
+
/**
|
|
4899
|
+
* Delete this sandbox and mark it as no longer running.
|
|
4900
|
+
*
|
|
4901
|
+
* After calling this, `isRunning` will be `false` and the sandbox
|
|
4902
|
+
* cannot be used again.
|
|
4903
|
+
*/
|
|
4904
|
+
async close() {
|
|
4905
|
+
await this.#sandbox.delete();
|
|
4906
|
+
this.#isRunning = false;
|
|
4907
|
+
}
|
|
4908
|
+
/**
|
|
4909
|
+
* Create and return a new LangSmithSandbox in one step.
|
|
4910
|
+
*
|
|
4911
|
+
* This is the recommended way to create a sandbox — no need to import
|
|
4912
|
+
* anything from `langsmith/experimental/sandbox` directly.
|
|
4913
|
+
*
|
|
4914
|
+
* @example
|
|
4915
|
+
* ```typescript
|
|
4916
|
+
* const sandbox = await LangSmithSandbox.create({ templateName: "deepagents" });
|
|
4917
|
+
* try {
|
|
4918
|
+
* const agent = createDeepAgent({ model, backend: sandbox });
|
|
4919
|
+
* await agent.invoke({ messages: [...] });
|
|
4920
|
+
* } finally {
|
|
4921
|
+
* await sandbox.close();
|
|
4922
|
+
* }
|
|
4923
|
+
* ```
|
|
4924
|
+
*/
|
|
4925
|
+
static async create(options = {}) {
|
|
4926
|
+
const { templateName = "deepagents", apiKey = process.env.LANGSMITH_API_KEY, defaultTimeout, ...createSandboxOptions } = options;
|
|
4927
|
+
return new LangSmithSandbox({
|
|
4928
|
+
sandbox: await new langsmith_experimental_sandbox.SandboxClient({ apiKey }).createSandbox(templateName, createSandboxOptions),
|
|
4929
|
+
defaultTimeout
|
|
4930
|
+
});
|
|
4931
|
+
}
|
|
4932
|
+
};
|
|
4933
|
+
//#endregion
|
|
4934
|
+
//#region src/errors.ts
|
|
4935
|
+
const CONFIGURATION_ERROR_SYMBOL = Symbol.for("deepagents.configuration_error");
|
|
4936
|
+
/**
|
|
4937
|
+
* Thrown when `createDeepAgent` receives invalid configuration.
|
|
4938
|
+
*
|
|
4939
|
+
* Follows the same pattern as {@link SandboxError}: a human-readable
|
|
4940
|
+
* `message`, a structured `code` for programmatic handling, and a
|
|
4941
|
+
* static `isInstance` guard that works across realms.
|
|
4942
|
+
*
|
|
4943
|
+
* @example
|
|
4944
|
+
* ```typescript
|
|
4945
|
+
* try {
|
|
4946
|
+
* createDeepAgent({ tools: [myTool] });
|
|
4947
|
+
* } catch (error) {
|
|
4948
|
+
* if (ConfigurationError.isInstance(error)) {
|
|
4949
|
+
* switch (error.code) {
|
|
4950
|
+
* case "TOOL_NAME_COLLISION":
|
|
4951
|
+
* console.error("Rename your tool:", error.message);
|
|
4952
|
+
* break;
|
|
4953
|
+
* }
|
|
4954
|
+
* }
|
|
4955
|
+
* }
|
|
4956
|
+
* ```
|
|
4957
|
+
*/
|
|
4958
|
+
var ConfigurationError = class ConfigurationError extends Error {
|
|
4959
|
+
[CONFIGURATION_ERROR_SYMBOL] = true;
|
|
4960
|
+
name = "ConfigurationError";
|
|
4961
|
+
constructor(message, code, cause) {
|
|
4962
|
+
super(message);
|
|
4963
|
+
this.code = code;
|
|
4964
|
+
this.cause = cause;
|
|
4965
|
+
Object.setPrototypeOf(this, ConfigurationError.prototype);
|
|
4966
|
+
}
|
|
4967
|
+
static isInstance(error) {
|
|
4968
|
+
return typeof error === "object" && error !== null && error[CONFIGURATION_ERROR_SYMBOL] === true;
|
|
4969
|
+
}
|
|
4970
|
+
};
|
|
4812
4971
|
//#endregion
|
|
4813
4972
|
//#region src/middleware/cache.ts
|
|
4814
4973
|
/**
|
|
@@ -4852,10 +5011,14 @@ function createCacheBreakpointMiddleware() {
|
|
|
4852
5011
|
}
|
|
4853
5012
|
});
|
|
4854
5013
|
}
|
|
4855
|
-
|
|
4856
5014
|
//#endregion
|
|
4857
5015
|
//#region src/agent.ts
|
|
4858
5016
|
const BASE_PROMPT = `In order to complete the objective that the user asks of you, you have access to a number of standard tools.`;
|
|
5017
|
+
const BUILTIN_TOOL_NAMES = new Set([
|
|
5018
|
+
...FILESYSTEM_TOOL_NAMES,
|
|
5019
|
+
"task",
|
|
5020
|
+
"write_todos"
|
|
5021
|
+
]);
|
|
4859
5022
|
/**
|
|
4860
5023
|
* Detect whether a model is an Anthropic model.
|
|
4861
5024
|
* Used to gate Anthropic-specific prompt caching optimizations (cache_control breakpoints).
|
|
@@ -4901,6 +5064,8 @@ function isAnthropicModel(model) {
|
|
|
4901
5064
|
*/
|
|
4902
5065
|
function createDeepAgent(params = {}) {
|
|
4903
5066
|
const { model = "claude-sonnet-4-5-20250929", tools = [], systemPrompt, middleware: customMiddleware = [], subagents = [], responseFormat, contextSchema, checkpointer, store, backend, interruptOn, name, memory, skills } = params;
|
|
5067
|
+
const collidingTools = tools.map((t) => t.name).filter((n) => typeof n === "string" && BUILTIN_TOOL_NAMES.has(n));
|
|
5068
|
+
if (collidingTools.length > 0) throw new ConfigurationError(`Tool name(s) [${collidingTools.join(", ")}] conflict with built-in tools. Rename your custom tools to avoid this.`, "TOOL_NAME_COLLISION");
|
|
4904
5069
|
const anthropicModel = isAnthropicModel(model);
|
|
4905
5070
|
const finalSystemPrompt = new langchain.SystemMessage({ content: systemPrompt ? typeof systemPrompt === "string" ? [{
|
|
4906
5071
|
type: "text",
|
|
@@ -4919,7 +5084,7 @@ function createDeepAgent(params = {}) {
|
|
|
4919
5084
|
* Create backend configuration for filesystem middleware
|
|
4920
5085
|
* If no backend is provided, use a factory that creates a StateBackend
|
|
4921
5086
|
*/
|
|
4922
|
-
const filesystemBackend = backend ? backend : (
|
|
5087
|
+
const filesystemBackend = backend ? backend : (runtime) => new StateBackend(runtime);
|
|
4923
5088
|
/**
|
|
4924
5089
|
* Skills middleware (created conditionally for runtime use)
|
|
4925
5090
|
*/
|
|
@@ -4982,10 +5147,6 @@ function createDeepAgent(params = {}) {
|
|
|
4982
5147
|
model,
|
|
4983
5148
|
backend: filesystemBackend
|
|
4984
5149
|
}),
|
|
4985
|
-
(0, langchain.anthropicPromptCachingMiddleware)({
|
|
4986
|
-
unsupportedModelBehavior: "ignore",
|
|
4987
|
-
minMessagesToCache: 1
|
|
4988
|
-
}),
|
|
4989
5150
|
createPatchToolCallsMiddleware()
|
|
4990
5151
|
];
|
|
4991
5152
|
/**
|
|
@@ -5008,11 +5169,17 @@ function createDeepAgent(params = {}) {
|
|
|
5008
5169
|
createSubAgentMiddleware({
|
|
5009
5170
|
defaultModel: model,
|
|
5010
5171
|
defaultTools: tools,
|
|
5011
|
-
defaultMiddleware: [...subagentMiddleware, ...anthropicModel ? [
|
|
5172
|
+
defaultMiddleware: [...subagentMiddleware, ...anthropicModel ? [(0, langchain.anthropicPromptCachingMiddleware)({
|
|
5173
|
+
unsupportedModelBehavior: "ignore",
|
|
5174
|
+
minMessagesToCache: 1
|
|
5175
|
+
}), createCacheBreakpointMiddleware()] : []],
|
|
5012
5176
|
generalPurposeMiddleware: [
|
|
5013
5177
|
...subagentMiddleware,
|
|
5014
5178
|
...skillsMiddlewareArray,
|
|
5015
|
-
...anthropicModel ? [
|
|
5179
|
+
...anthropicModel ? [(0, langchain.anthropicPromptCachingMiddleware)({
|
|
5180
|
+
unsupportedModelBehavior: "ignore",
|
|
5181
|
+
minMessagesToCache: 1
|
|
5182
|
+
}), createCacheBreakpointMiddleware()] : []
|
|
5016
5183
|
],
|
|
5017
5184
|
defaultInterruptOn: interruptOn,
|
|
5018
5185
|
subagents: processedSubagents,
|
|
@@ -5022,17 +5189,16 @@ function createDeepAgent(params = {}) {
|
|
|
5022
5189
|
model,
|
|
5023
5190
|
backend: filesystemBackend
|
|
5024
5191
|
}),
|
|
5025
|
-
(0, langchain.anthropicPromptCachingMiddleware)({
|
|
5026
|
-
unsupportedModelBehavior: "ignore",
|
|
5027
|
-
minMessagesToCache: 1
|
|
5028
|
-
}),
|
|
5029
5192
|
createPatchToolCallsMiddleware()
|
|
5030
5193
|
],
|
|
5031
5194
|
...skillsMiddlewareArray,
|
|
5032
|
-
...
|
|
5195
|
+
...customMiddleware,
|
|
5196
|
+
...anthropicModel ? [(0, langchain.anthropicPromptCachingMiddleware)({
|
|
5197
|
+
unsupportedModelBehavior: "ignore",
|
|
5198
|
+
minMessagesToCache: 1
|
|
5199
|
+
}), createCacheBreakpointMiddleware()] : [],
|
|
5033
5200
|
...memoryMiddlewareArray,
|
|
5034
|
-
...interruptOn ? [(0, langchain.humanInTheLoopMiddleware)({ interruptOn })] : []
|
|
5035
|
-
...customMiddleware
|
|
5201
|
+
...interruptOn ? [(0, langchain.humanInTheLoopMiddleware)({ interruptOn })] : []
|
|
5036
5202
|
],
|
|
5037
5203
|
...responseFormat != null && { responseFormat },
|
|
5038
5204
|
contextSchema,
|
|
@@ -5044,7 +5210,6 @@ function createDeepAgent(params = {}) {
|
|
|
5044
5210
|
metadata: { ls_integration: "deepagents" }
|
|
5045
5211
|
});
|
|
5046
5212
|
}
|
|
5047
|
-
|
|
5048
5213
|
//#endregion
|
|
5049
5214
|
//#region src/config.ts
|
|
5050
5215
|
/**
|
|
@@ -5138,7 +5303,6 @@ function createSettings(options = {}) {
|
|
|
5138
5303
|
}
|
|
5139
5304
|
};
|
|
5140
5305
|
}
|
|
5141
|
-
|
|
5142
5306
|
//#endregion
|
|
5143
5307
|
//#region src/middleware/agent-memory.ts
|
|
5144
5308
|
/**
|
|
@@ -5365,38 +5529,6 @@ function createAgentMemoryMiddleware(options) {
|
|
|
5365
5529
|
}
|
|
5366
5530
|
});
|
|
5367
5531
|
}
|
|
5368
|
-
|
|
5369
|
-
//#endregion
|
|
5370
|
-
//#region src/skills/loader.ts
|
|
5371
|
-
/**
|
|
5372
|
-
* Skill loader for parsing and loading agent skills from SKILL.md files.
|
|
5373
|
-
*
|
|
5374
|
-
* This module implements Anthropic's agent skills pattern with YAML frontmatter parsing.
|
|
5375
|
-
* Each skill is a directory containing a SKILL.md file with:
|
|
5376
|
-
* - YAML frontmatter (name, description required)
|
|
5377
|
-
* - Markdown instructions for the agent
|
|
5378
|
-
* - Optional supporting files (scripts, configs, etc.)
|
|
5379
|
-
*
|
|
5380
|
-
* @example
|
|
5381
|
-
* ```markdown
|
|
5382
|
-
* ---
|
|
5383
|
-
* name: web-research
|
|
5384
|
-
* description: Structured approach to conducting thorough web research
|
|
5385
|
-
* ---
|
|
5386
|
-
*
|
|
5387
|
-
* # Web Research Skill
|
|
5388
|
-
*
|
|
5389
|
-
* ## When to Use
|
|
5390
|
-
* - User asks you to research a topic
|
|
5391
|
-
* ...
|
|
5392
|
-
* ```
|
|
5393
|
-
*
|
|
5394
|
-
* @see https://agentskills.io/specification
|
|
5395
|
-
*/
|
|
5396
|
-
/** Maximum size for SKILL.md files (10MB) */
|
|
5397
|
-
const MAX_SKILL_FILE_SIZE$1 = 10 * 1024 * 1024;
|
|
5398
|
-
/** Agent Skills spec constraints */
|
|
5399
|
-
const MAX_SKILL_NAME_LENGTH$1 = 64;
|
|
5400
5532
|
const MAX_SKILL_DESCRIPTION_LENGTH$1 = 1024;
|
|
5401
5533
|
/** Pattern for validating skill names per Agent Skills spec */
|
|
5402
5534
|
const SKILL_NAME_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
@@ -5441,7 +5573,7 @@ function validateSkillName(name, directoryName) {
|
|
|
5441
5573
|
valid: false,
|
|
5442
5574
|
error: "name is required"
|
|
5443
5575
|
};
|
|
5444
|
-
if (name.length >
|
|
5576
|
+
if (name.length > 64) return {
|
|
5445
5577
|
valid: false,
|
|
5446
5578
|
error: "name exceeds 64 characters"
|
|
5447
5579
|
};
|
|
@@ -5481,7 +5613,7 @@ function parseFrontmatter(content) {
|
|
|
5481
5613
|
function parseSkillMetadata(skillMdPath, source) {
|
|
5482
5614
|
try {
|
|
5483
5615
|
const stats = node_fs.default.statSync(skillMdPath);
|
|
5484
|
-
if (stats.size >
|
|
5616
|
+
if (stats.size > 10485760) {
|
|
5485
5617
|
console.warn(`Skipping ${skillMdPath}: file too large (${stats.size} bytes)`);
|
|
5486
5618
|
return null;
|
|
5487
5619
|
}
|
|
@@ -5500,7 +5632,7 @@ function parseSkillMetadata(skillMdPath, source) {
|
|
|
5500
5632
|
const validation = validateSkillName(String(name), directoryName);
|
|
5501
5633
|
if (!validation.valid) console.warn(`Skill '${name}' in ${skillMdPath} does not follow Agent Skills spec: ${validation.error}. Consider renaming to be spec-compliant.`);
|
|
5502
5634
|
let descriptionStr = String(description);
|
|
5503
|
-
if (descriptionStr.length >
|
|
5635
|
+
if (descriptionStr.length > 1024) {
|
|
5504
5636
|
console.warn(`Description exceeds ${MAX_SKILL_DESCRIPTION_LENGTH$1} chars in ${skillMdPath}, truncating`);
|
|
5505
5637
|
descriptionStr = descriptionStr.slice(0, MAX_SKILL_DESCRIPTION_LENGTH$1);
|
|
5506
5638
|
}
|
|
@@ -5588,14 +5720,15 @@ function listSkills(options) {
|
|
|
5588
5720
|
}
|
|
5589
5721
|
return Array.from(allSkills.values());
|
|
5590
5722
|
}
|
|
5591
|
-
|
|
5592
5723
|
//#endregion
|
|
5593
5724
|
exports.BaseSandbox = BaseSandbox;
|
|
5594
5725
|
exports.CompositeBackend = CompositeBackend;
|
|
5726
|
+
exports.ConfigurationError = ConfigurationError;
|
|
5595
5727
|
exports.DEFAULT_GENERAL_PURPOSE_DESCRIPTION = DEFAULT_GENERAL_PURPOSE_DESCRIPTION;
|
|
5596
5728
|
exports.DEFAULT_SUBAGENT_PROMPT = DEFAULT_SUBAGENT_PROMPT;
|
|
5597
5729
|
exports.FilesystemBackend = FilesystemBackend;
|
|
5598
5730
|
exports.GENERAL_PURPOSE_SUBAGENT = GENERAL_PURPOSE_SUBAGENT;
|
|
5731
|
+
exports.LangSmithSandbox = LangSmithSandbox;
|
|
5599
5732
|
exports.LocalShellBackend = LocalShellBackend;
|
|
5600
5733
|
exports.MAX_SKILL_DESCRIPTION_LENGTH = MAX_SKILL_DESCRIPTION_LENGTH;
|
|
5601
5734
|
exports.MAX_SKILL_FILE_SIZE = MAX_SKILL_FILE_SIZE;
|
|
@@ -5619,4 +5752,6 @@ exports.findProjectRoot = findProjectRoot;
|
|
|
5619
5752
|
exports.isSandboxBackend = isSandboxBackend;
|
|
5620
5753
|
exports.listSkills = listSkills;
|
|
5621
5754
|
exports.parseSkillMetadata = parseSkillMetadata;
|
|
5755
|
+
exports.resolveBackend = resolveBackend;
|
|
5756
|
+
|
|
5622
5757
|
//# sourceMappingURL=index.cjs.map
|