deepagents 1.8.4 → 1.8.5

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
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
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
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
- key = keys[i];
13
- if (!__hasOwnProp.call(to, key) && key !== except) {
14
- __defProp(to, key, {
15
- get: ((k) => from[k]).bind(null, key),
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,6 @@ var SandboxError = class SandboxError extends Error {
112
106
  return typeof error === "object" && error !== null && error[SANDBOX_ERROR_SYMBOL] === true;
113
107
  }
114
108
  };
115
-
116
109
  //#endregion
117
110
  //#region src/backends/utils.ts
118
111
  /**
@@ -124,7 +117,6 @@ var SandboxError = class SandboxError extends Error {
124
117
  */
125
118
  const EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents";
126
119
  const MAX_LINE_LENGTH = 1e4;
127
- const LINE_NUMBER_WIDTH = 6;
128
120
  const TOOL_RESULT_TOKEN_LIMIT = 2e4;
129
121
  const TRUNCATION_GUIDANCE = "... [results truncated, try being more specific with your parameters]";
130
122
  /**
@@ -154,17 +146,17 @@ function formatContentWithLineNumbers(content, startLine = 1) {
154
146
  for (let i = 0; i < lines.length; i++) {
155
147
  const line = lines[i];
156
148
  const lineNum = i + startLine;
157
- if (line.length <= MAX_LINE_LENGTH) resultLines.push(`${lineNum.toString().padStart(LINE_NUMBER_WIDTH)}\t${line}`);
149
+ if (line.length <= 1e4) resultLines.push(`${lineNum.toString().padStart(6)}\t${line}`);
158
150
  else {
159
151
  const numChunks = Math.ceil(line.length / MAX_LINE_LENGTH);
160
152
  for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
161
153
  const start = chunkIdx * MAX_LINE_LENGTH;
162
154
  const end = Math.min(start + MAX_LINE_LENGTH, line.length);
163
155
  const chunk = line.substring(start, end);
164
- if (chunkIdx === 0) resultLines.push(`${lineNum.toString().padStart(LINE_NUMBER_WIDTH)}\t${chunk}`);
156
+ if (chunkIdx === 0) resultLines.push(`${lineNum.toString().padStart(6)}\t${chunk}`);
165
157
  else {
166
158
  const continuationMarker = `${lineNum}.${chunkIdx}`;
167
- resultLines.push(`${continuationMarker.padStart(LINE_NUMBER_WIDTH)}\t${chunk}`);
159
+ resultLines.push(`${continuationMarker.padStart(6)}\t${chunk}`);
168
160
  }
169
161
  }
170
162
  }
@@ -267,13 +259,13 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
267
259
  function truncateIfTooLong(result) {
268
260
  if (Array.isArray(result)) {
269
261
  const totalChars = result.reduce((sum, item) => sum + item.length, 0);
270
- if (totalChars > TOOL_RESULT_TOKEN_LIMIT * 4) {
262
+ if (totalChars > 2e4 * 4) {
271
263
  const truncateAt = Math.floor(result.length * TOOL_RESULT_TOKEN_LIMIT * 4 / totalChars);
272
264
  return [...result.slice(0, truncateAt), TRUNCATION_GUIDANCE];
273
265
  }
274
266
  return result;
275
267
  }
276
- if (result.length > TOOL_RESULT_TOKEN_LIMIT * 4) return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n... [results truncated, try being more specific with your parameters]";
268
+ 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
269
  return result;
278
270
  }
279
271
  /**
@@ -381,7 +373,6 @@ function grepMatchesFromFiles(files, pattern, path$9 = null, glob = null) {
381
373
  }
382
374
  return matches;
383
375
  }
384
-
385
376
  //#endregion
386
377
  //#region src/backends/state.ts
387
378
  /**
@@ -581,7 +572,6 @@ var StateBackend = class {
581
572
  return responses;
582
573
  }
583
574
  };
584
-
585
575
  //#endregion
586
576
  //#region src/middleware/fs.ts
587
577
  /**
@@ -614,6 +604,20 @@ var StateBackend = class {
614
604
  * These tools return minimal confirmation messages and are never expected to produce
615
605
  * output large enough to exceed token limits, so checking them would be unnecessary.
616
606
  */
607
+ /**
608
+ * All tool names registered by FilesystemMiddleware.
609
+ * This is the single source of truth — used by createDeepAgent to detect
610
+ * collisions with user-supplied tools at construction time.
611
+ */
612
+ const FILESYSTEM_TOOL_NAMES = [
613
+ "ls",
614
+ "read_file",
615
+ "write_file",
616
+ "edit_file",
617
+ "glob",
618
+ "grep",
619
+ "execute"
620
+ ];
617
621
  const TOOLS_EXCLUDED_FROM_EVICTION = [
618
622
  "ls",
619
623
  "glob",
@@ -623,17 +627,6 @@ const TOOLS_EXCLUDED_FROM_EVICTION = [
623
627
  "write_file"
624
628
  ];
625
629
  /**
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
630
  * Template for truncation message in read_file.
638
631
  * {file_path} will be filled in at runtime.
639
632
  */
@@ -875,13 +868,13 @@ function createReadFileTool(backend, options) {
875
868
  state: (0, _langchain_langgraph.getCurrentTaskInput)(config),
876
869
  store: config.store
877
870
  });
878
- const { file_path, offset = DEFAULT_READ_LINE_OFFSET, limit = DEFAULT_READ_LINE_LIMIT } = input;
871
+ const { file_path, offset = 0, limit = 100 } = input;
879
872
  let result = await resolvedBackend.read(file_path, offset, limit);
880
873
  const lines = result.split("\n");
881
874
  if (lines.length > limit) result = lines.slice(0, limit).join("\n");
882
- if (toolTokenLimitBeforeEvict && result.length >= NUM_CHARS_PER_TOKEN * toolTokenLimitBeforeEvict) {
875
+ if (toolTokenLimitBeforeEvict && result.length >= 4 * toolTokenLimitBeforeEvict) {
883
876
  const truncationMsg = READ_FILE_TRUNCATION_MSG.replace("{file_path}", file_path);
884
- const maxContentLength = NUM_CHARS_PER_TOKEN * toolTokenLimitBeforeEvict - truncationMsg.length;
877
+ const maxContentLength = 4 * toolTokenLimitBeforeEvict - truncationMsg.length;
885
878
  result = result.substring(0, maxContentLength) + truncationMsg;
886
879
  }
887
880
  return result;
@@ -890,8 +883,8 @@ function createReadFileTool(backend, options) {
890
883
  description: customDescription || READ_FILE_TOOL_DESCRIPTION,
891
884
  schema: zod_v4.z.object({
892
885
  file_path: zod_v4.z.string().describe("Absolute path to the file to read"),
893
- offset: zod_v4.z.coerce.number().optional().default(DEFAULT_READ_LINE_OFFSET).describe("Line offset to start reading from (0-indexed)"),
894
- limit: zod_v4.z.coerce.number().optional().default(DEFAULT_READ_LINE_LIMIT).describe("Maximum number of lines to read")
886
+ offset: zod_v4.z.coerce.number().optional().default(0).describe("Line offset to start reading from (0-indexed)"),
887
+ limit: zod_v4.z.coerce.number().optional().default(100).describe("Maximum number of lines to read")
895
888
  })
896
889
  });
897
890
  }
@@ -1055,21 +1048,22 @@ function createExecuteTool(backend, options) {
1055
1048
  function createFilesystemMiddleware(options = {}) {
1056
1049
  const { backend = (stateAndStore) => new StateBackend(stateAndStore), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4 } = options;
1057
1050
  const baseSystemPrompt = customSystemPrompt || FILESYSTEM_SYSTEM_PROMPT;
1051
+ const allToolsByName = {
1052
+ ls: createLsTool(backend, { customDescription: customToolDescriptions?.ls }),
1053
+ read_file: createReadFileTool(backend, {
1054
+ customDescription: customToolDescriptions?.read_file,
1055
+ toolTokenLimitBeforeEvict
1056
+ }),
1057
+ write_file: createWriteFileTool(backend, { customDescription: customToolDescriptions?.write_file }),
1058
+ edit_file: createEditFileTool(backend, { customDescription: customToolDescriptions?.edit_file }),
1059
+ glob: createGlobTool(backend, { customDescription: customToolDescriptions?.glob }),
1060
+ grep: createGrepTool(backend, { customDescription: customToolDescriptions?.grep }),
1061
+ execute: createExecuteTool(backend, { customDescription: customToolDescriptions?.execute })
1062
+ };
1058
1063
  return (0, langchain.createMiddleware)({
1059
1064
  name: "FilesystemMiddleware",
1060
1065
  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
- ],
1066
+ tools: Object.values(allToolsByName),
1073
1067
  wrapModelCall: async (request, handler) => {
1074
1068
  const supportsExecution = isSandboxBackend(getBackend(backend, {
1075
1069
  state: request.state || {},
@@ -1092,7 +1086,7 @@ function createFilesystemMiddleware(options = {}) {
1092
1086
  if (toolName && TOOLS_EXCLUDED_FROM_EVICTION.includes(toolName)) return handler(request);
1093
1087
  const result = await handler(request);
1094
1088
  async function processToolMessage(msg, toolTokenLimitBeforeEvict) {
1095
- if (typeof msg.content === "string" && msg.content.length > toolTokenLimitBeforeEvict * NUM_CHARS_PER_TOKEN) {
1089
+ if (typeof msg.content === "string" && msg.content.length > toolTokenLimitBeforeEvict * 4) {
1096
1090
  const resolvedBackend = getBackend(backend, {
1097
1091
  state: request.state || {},
1098
1092
  store: request.runtime?.store
@@ -1156,7 +1150,6 @@ function createFilesystemMiddleware(options = {}) {
1156
1150
  }
1157
1151
  });
1158
1152
  }
1159
-
1160
1153
  //#endregion
1161
1154
  //#region src/middleware/subagents.ts
1162
1155
  /**
@@ -1537,12 +1530,23 @@ function createSubAgentMiddleware(options) {
1537
1530
  }
1538
1531
  });
1539
1532
  }
1540
-
1541
1533
  //#endregion
1542
1534
  //#region src/middleware/patch_tool_calls.ts
1543
1535
  /**
1544
- * Patch dangling tool calls in a messages array.
1545
- * Returns the patched messages array and a flag indicating if patching was needed.
1536
+ * Patch tool call / tool response parity in a messages array.
1537
+ *
1538
+ * Ensures strict 1:1 correspondence between AIMessage tool_calls and
1539
+ * ToolMessage responses:
1540
+ *
1541
+ * 1. **Dangling tool_calls** — an AIMessage contains a tool_call with no
1542
+ * matching ToolMessage anywhere after it. A synthetic cancellation
1543
+ * ToolMessage is inserted immediately after the AIMessage.
1544
+ *
1545
+ * 2. **Orphaned ToolMessages** — a ToolMessage whose `tool_call_id` does not
1546
+ * match any tool_call in a preceding AIMessage. The ToolMessage is removed.
1547
+ *
1548
+ * Both directions are required for providers that enforce strict parity
1549
+ * (e.g. Google Gemini returns 400 INVALID_ARGUMENT otherwise).
1546
1550
  *
1547
1551
  * @param messages - The messages array to patch
1548
1552
  * @returns Object with patched messages and needsPatch flag
@@ -1552,13 +1556,23 @@ function patchDanglingToolCalls(messages) {
1552
1556
  patchedMessages: [],
1553
1557
  needsPatch: false
1554
1558
  };
1559
+ const allToolCallIds = /* @__PURE__ */ new Set();
1560
+ for (const msg of messages) if (langchain.AIMessage.isInstance(msg) && msg.tool_calls != null) {
1561
+ for (const tc of msg.tool_calls) if (tc.id) allToolCallIds.add(tc.id);
1562
+ }
1555
1563
  const patchedMessages = [];
1556
1564
  let needsPatch = false;
1557
1565
  for (let i = 0; i < messages.length; i++) {
1558
1566
  const msg = messages[i];
1567
+ if (langchain.ToolMessage.isInstance(msg)) {
1568
+ if (!allToolCallIds.has(msg.tool_call_id)) {
1569
+ needsPatch = true;
1570
+ continue;
1571
+ }
1572
+ }
1559
1573
  patchedMessages.push(msg);
1560
1574
  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)) {
1575
+ 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
1576
  needsPatch = true;
1563
1577
  const toolMsg = `Tool call ${toolCall.name} with id ${toolCall.id} was cancelled - another message came in before it could be completed.`;
1564
1578
  patchedMessages.push(new langchain.ToolMessage({
@@ -1575,11 +1589,18 @@ function patchDanglingToolCalls(messages) {
1575
1589
  };
1576
1590
  }
1577
1591
  /**
1578
- * Create middleware that patches dangling tool calls in the messages history.
1592
+ * Create middleware that enforces strict tool call / tool response parity in
1593
+ * the messages history.
1594
+ *
1595
+ * Two kinds of violations are repaired:
1596
+ * 1. **Dangling tool_calls** — an AIMessage contains tool_calls with no
1597
+ * matching ToolMessage responses. Synthetic cancellation ToolMessages are
1598
+ * injected so every tool_call has a response.
1599
+ * 2. **Orphaned ToolMessages** — a ToolMessage exists whose `tool_call_id`
1600
+ * does not match any tool_call in a preceding AIMessage. These are removed.
1579
1601
  *
1580
- * When an AI message contains tool_calls but subsequent messages don't include
1581
- * the corresponding ToolMessage responses, this middleware adds synthetic
1582
- * ToolMessages saying the tool call was cancelled.
1602
+ * This is critical for providers like Google Gemini that reject requests with
1603
+ * mismatched function call / function response counts (400 INVALID_ARGUMENT).
1583
1604
  *
1584
1605
  * This middleware patches in two places:
1585
1606
  * 1. `beforeAgent`: Patches state at the start of the agent loop (handles most cases)
@@ -1587,7 +1608,7 @@ function patchDanglingToolCalls(messages) {
1587
1608
  * edge cases like HITL rejection during graph resume where state updates from
1588
1609
  * beforeAgent may not be applied in time)
1589
1610
  *
1590
- * @returns AgentMiddleware that patches dangling tool calls
1611
+ * @returns AgentMiddleware that enforces tool call / response parity
1591
1612
  *
1592
1613
  * @example
1593
1614
  * ```typescript
@@ -1625,7 +1646,6 @@ function createPatchToolCallsMiddleware() {
1625
1646
  }
1626
1647
  });
1627
1648
  }
1628
-
1629
1649
  //#endregion
1630
1650
  //#region src/values.ts
1631
1651
  /**
@@ -1660,7 +1680,6 @@ const filesValue = new _langchain_langgraph.ReducedValue(zod.z.record(zod.z.stri
1660
1680
  inputSchema: zod.z.record(zod.z.string(), FileDataSchema.nullable()).optional(),
1661
1681
  reducer: fileDataReducer
1662
1682
  });
1663
-
1664
1683
  //#endregion
1665
1684
  //#region src/middleware/memory.ts
1666
1685
  /**
@@ -1879,7 +1898,6 @@ function createMemoryMiddleware(options) {
1879
1898
  }
1880
1899
  });
1881
1900
  }
1882
-
1883
1901
  //#endregion
1884
1902
  //#region src/middleware/skills.ts
1885
1903
  /**
@@ -1925,7 +1943,6 @@ function createMemoryMiddleware(options) {
1925
1943
  const MAX_SKILL_FILE_SIZE = 10 * 1024 * 1024;
1926
1944
  const MAX_SKILL_NAME_LENGTH = 64;
1927
1945
  const MAX_SKILL_DESCRIPTION_LENGTH = 1024;
1928
- const MAX_SKILL_COMPATIBILITY_LENGTH = 500;
1929
1946
  /**
1930
1947
  * Zod schema for a single skill metadata entry.
1931
1948
  */
@@ -2035,7 +2052,7 @@ function validateSkillName$1(name, directoryName) {
2035
2052
  valid: false,
2036
2053
  error: "name is required"
2037
2054
  };
2038
- if (name.length > MAX_SKILL_NAME_LENGTH) return {
2055
+ if (name.length > 64) return {
2039
2056
  valid: false,
2040
2057
  error: "name exceeds 64 characters"
2041
2058
  };
@@ -2109,7 +2126,7 @@ function formatSkillAnnotations(skill) {
2109
2126
  * validation errors occur
2110
2127
  */
2111
2128
  function parseSkillMetadataFromContent(content, skillPath, directoryName) {
2112
- if (content.length > MAX_SKILL_FILE_SIZE) {
2129
+ if (content.length > 10485760) {
2113
2130
  console.warn(`Skipping ${skillPath}: content too large (${content.length} bytes)`);
2114
2131
  return null;
2115
2132
  }
@@ -2139,7 +2156,7 @@ function parseSkillMetadataFromContent(content, skillPath, directoryName) {
2139
2156
  const validation = validateSkillName$1(name, directoryName);
2140
2157
  if (!validation.valid) console.warn(`Skill '${name}' in ${skillPath} does not follow Agent Skills specification: ${validation.error}. Consider renaming for spec compliance.`);
2141
2158
  let descriptionStr = description;
2142
- if (descriptionStr.length > MAX_SKILL_DESCRIPTION_LENGTH) {
2159
+ if (descriptionStr.length > 1024) {
2143
2160
  console.warn(`Description exceeds ${MAX_SKILL_DESCRIPTION_LENGTH} characters in ${skillPath}, truncating`);
2144
2161
  descriptionStr = descriptionStr.slice(0, MAX_SKILL_DESCRIPTION_LENGTH);
2145
2162
  }
@@ -2149,9 +2166,9 @@ function parseSkillMetadataFromContent(content, skillPath, directoryName) {
2149
2166
  else allowedTools = String(rawTools).split(/\s+/).filter(Boolean);
2150
2167
  else allowedTools = [];
2151
2168
  let compatibilityStr = String(frontmatterData.compatibility ?? "").trim() || null;
2152
- if (compatibilityStr && compatibilityStr.length > MAX_SKILL_COMPATIBILITY_LENGTH) {
2153
- console.warn(`Compatibility exceeds ${MAX_SKILL_COMPATIBILITY_LENGTH} characters in ${skillPath}, truncating`);
2154
- compatibilityStr = compatibilityStr.slice(0, MAX_SKILL_COMPATIBILITY_LENGTH);
2169
+ if (compatibilityStr && compatibilityStr.length > 500) {
2170
+ console.warn(`Compatibility exceeds 500 characters in ${skillPath}, truncating`);
2171
+ compatibilityStr = compatibilityStr.slice(0, 500);
2155
2172
  }
2156
2173
  return {
2157
2174
  name,
@@ -2294,7 +2311,6 @@ function createSkillsMiddleware(options) {
2294
2311
  }
2295
2312
  });
2296
2313
  }
2297
-
2298
2314
  //#endregion
2299
2315
  //#region src/middleware/utils.ts
2300
2316
  /**
@@ -2302,7 +2318,6 @@ function createSkillsMiddleware(options) {
2302
2318
  *
2303
2319
  * This module provides shared helpers used across middleware implementations.
2304
2320
  */
2305
-
2306
2321
  //#endregion
2307
2322
  //#region src/middleware/summarization.ts
2308
2323
  /**
@@ -2507,7 +2522,7 @@ function createSummarizationMiddleware(options) {
2507
2522
  */
2508
2523
  function getSessionId(state) {
2509
2524
  if (state._summarizationSessionId) return state._summarizationSessionId;
2510
- if (!sessionId) sessionId = `session_${(0, uuid.v4)().substring(0, 8)}`;
2525
+ if (!sessionId) sessionId = `session_${crypto.randomUUID().substring(0, 8)}`;
2511
2526
  return sessionId;
2512
2527
  }
2513
2528
  /**
@@ -2992,7 +3007,6 @@ ${summary}
2992
3007
  }
2993
3008
  });
2994
3009
  }
2995
-
2996
3010
  //#endregion
2997
3011
  //#region src/backends/store.ts
2998
3012
  const NAMESPACE_COMPONENT_RE = /^[A-Za-z0-9\-_.@+:~]+$/;
@@ -3336,7 +3350,6 @@ var StoreBackend = class {
3336
3350
  return responses;
3337
3351
  }
3338
3352
  };
3339
-
3340
3353
  //#endregion
3341
3354
  //#region src/backends/filesystem.ts
3342
3355
  /**
@@ -3855,7 +3868,6 @@ var FilesystemBackend = class {
3855
3868
  return responses;
3856
3869
  }
3857
3870
  };
3858
-
3859
3871
  //#endregion
3860
3872
  //#region src/backends/composite.ts
3861
3873
  /**
@@ -4104,7 +4116,6 @@ var CompositeBackend = class {
4104
4116
  return results;
4105
4117
  }
4106
4118
  };
4107
-
4108
4119
  //#endregion
4109
4120
  //#region src/backends/local-shell.ts
4110
4121
  /**
@@ -4409,7 +4420,6 @@ var LocalShellBackend = class LocalShellBackend extends FilesystemBackend {
4409
4420
  return backend;
4410
4421
  }
4411
4422
  };
4412
-
4413
4423
  //#endregion
4414
4424
  //#region src/backends/sandbox.ts
4415
4425
  /**
@@ -4808,7 +4818,197 @@ var BaseSandbox = class {
4808
4818
  };
4809
4819
  }
4810
4820
  };
4811
-
4821
+ //#endregion
4822
+ //#region src/backends/langsmith.ts
4823
+ /**
4824
+ * LangSmith Sandbox backend for deepagents.
4825
+ *
4826
+ * @example
4827
+ * ```typescript
4828
+ * import { LangSmithSandbox, createDeepAgent } from "deepagents";
4829
+ *
4830
+ * const sandbox = await LangSmithSandbox.create({ templateName: "deepagents-cli" });
4831
+ *
4832
+ * const agent = createDeepAgent({ model, backend: sandbox });
4833
+ *
4834
+ * try {
4835
+ * await agent.invoke({ messages: [...] });
4836
+ * } finally {
4837
+ * await sandbox.close();
4838
+ * }
4839
+ * ```
4840
+ */
4841
+ /**
4842
+ * LangSmith Sandbox backend for deepagents.
4843
+ *
4844
+ * Extends `BaseSandbox` to provide command execution and file operations
4845
+ * via the LangSmith Sandbox API.
4846
+ *
4847
+ * Use the static `LangSmithSandbox.create()` factory for the simplest setup,
4848
+ * or construct directly with an existing `Sandbox` instance.
4849
+ */
4850
+ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
4851
+ #sandbox;
4852
+ #defaultTimeout;
4853
+ #isRunning = true;
4854
+ constructor(options) {
4855
+ super();
4856
+ this.#sandbox = options.sandbox;
4857
+ this.#defaultTimeout = options.defaultTimeout ?? 1800;
4858
+ }
4859
+ /** Whether the sandbox is currently active. */
4860
+ get isRunning() {
4861
+ return this.#isRunning;
4862
+ }
4863
+ /** Return the LangSmith sandbox name as the unique identifier. */
4864
+ get id() {
4865
+ return this.#sandbox.name;
4866
+ }
4867
+ /**
4868
+ * Execute a shell command in the LangSmith sandbox.
4869
+ *
4870
+ * @param command - Shell command string to execute
4871
+ * @param options.timeout - Override timeout in seconds; 0 disables timeout
4872
+ */
4873
+ async execute(command, options) {
4874
+ const effectiveTimeout = options?.timeout !== void 0 ? options.timeout : this.#defaultTimeout;
4875
+ const result = await this.#sandbox.run(command, { timeout: effectiveTimeout });
4876
+ const out = result.stdout ?? "";
4877
+ return {
4878
+ output: result.stderr ? out ? `${out}\n${result.stderr}` : result.stderr : out,
4879
+ exitCode: result.exit_code,
4880
+ truncated: false
4881
+ };
4882
+ }
4883
+ /**
4884
+ * Download files from the sandbox using LangSmith's native file read API.
4885
+ * @param paths - List of file paths to download
4886
+ * @returns List of FileDownloadResponse objects, one per input path
4887
+ */
4888
+ async downloadFiles(paths) {
4889
+ const responses = [];
4890
+ for (const path of paths) try {
4891
+ const content = await this.#sandbox.read(path);
4892
+ responses.push({
4893
+ path,
4894
+ content,
4895
+ error: null
4896
+ });
4897
+ } catch (err) {
4898
+ if (err instanceof langsmith_experimental_sandbox.LangSmithResourceNotFoundError) responses.push({
4899
+ path,
4900
+ content: null,
4901
+ error: "file_not_found"
4902
+ });
4903
+ else if (err instanceof langsmith_experimental_sandbox.LangSmithSandboxError) {
4904
+ const error = String(err.message).toLowerCase().includes("is a directory") ? "is_directory" : "file_not_found";
4905
+ responses.push({
4906
+ path,
4907
+ content: null,
4908
+ error
4909
+ });
4910
+ } else responses.push({
4911
+ path,
4912
+ content: null,
4913
+ error: "invalid_path"
4914
+ });
4915
+ }
4916
+ return responses;
4917
+ }
4918
+ /**
4919
+ * Upload files to the sandbox using LangSmith's native file write API.
4920
+ * @param files - List of [path, content] tuples to upload
4921
+ * @returns List of FileUploadResponse objects, one per input file
4922
+ */
4923
+ async uploadFiles(files) {
4924
+ const responses = [];
4925
+ for (const [path, content] of files) try {
4926
+ await this.#sandbox.write(path, content);
4927
+ responses.push({
4928
+ path,
4929
+ error: null
4930
+ });
4931
+ } catch {
4932
+ responses.push({
4933
+ path,
4934
+ error: "permission_denied"
4935
+ });
4936
+ }
4937
+ return responses;
4938
+ }
4939
+ /**
4940
+ * Delete this sandbox and mark it as no longer running.
4941
+ *
4942
+ * After calling this, `isRunning` will be `false` and the sandbox
4943
+ * cannot be used again.
4944
+ */
4945
+ async close() {
4946
+ await this.#sandbox.delete();
4947
+ this.#isRunning = false;
4948
+ }
4949
+ /**
4950
+ * Create and return a new LangSmithSandbox in one step.
4951
+ *
4952
+ * This is the recommended way to create a sandbox — no need to import
4953
+ * anything from `langsmith/experimental/sandbox` directly.
4954
+ *
4955
+ * @example
4956
+ * ```typescript
4957
+ * const sandbox = await LangSmithSandbox.create({ templateName: "deepagents" });
4958
+ * try {
4959
+ * const agent = createDeepAgent({ model, backend: sandbox });
4960
+ * await agent.invoke({ messages: [...] });
4961
+ * } finally {
4962
+ * await sandbox.close();
4963
+ * }
4964
+ * ```
4965
+ */
4966
+ static async create(options = {}) {
4967
+ const { templateName = "deepagents", apiKey = process.env.LANGSMITH_API_KEY, defaultTimeout } = options;
4968
+ return new LangSmithSandbox({
4969
+ sandbox: await new langsmith_experimental_sandbox.SandboxClient({ apiKey }).createSandbox(templateName),
4970
+ defaultTimeout
4971
+ });
4972
+ }
4973
+ };
4974
+ //#endregion
4975
+ //#region src/errors.ts
4976
+ const CONFIGURATION_ERROR_SYMBOL = Symbol.for("deepagents.configuration_error");
4977
+ /**
4978
+ * Thrown when `createDeepAgent` receives invalid configuration.
4979
+ *
4980
+ * Follows the same pattern as {@link SandboxError}: a human-readable
4981
+ * `message`, a structured `code` for programmatic handling, and a
4982
+ * static `isInstance` guard that works across realms.
4983
+ *
4984
+ * @example
4985
+ * ```typescript
4986
+ * try {
4987
+ * createDeepAgent({ tools: [myTool] });
4988
+ * } catch (error) {
4989
+ * if (ConfigurationError.isInstance(error)) {
4990
+ * switch (error.code) {
4991
+ * case "TOOL_NAME_COLLISION":
4992
+ * console.error("Rename your tool:", error.message);
4993
+ * break;
4994
+ * }
4995
+ * }
4996
+ * }
4997
+ * ```
4998
+ */
4999
+ var ConfigurationError = class ConfigurationError extends Error {
5000
+ [CONFIGURATION_ERROR_SYMBOL] = true;
5001
+ name = "ConfigurationError";
5002
+ constructor(message, code, cause) {
5003
+ super(message);
5004
+ this.code = code;
5005
+ this.cause = cause;
5006
+ Object.setPrototypeOf(this, ConfigurationError.prototype);
5007
+ }
5008
+ static isInstance(error) {
5009
+ return typeof error === "object" && error !== null && error[CONFIGURATION_ERROR_SYMBOL] === true;
5010
+ }
5011
+ };
4812
5012
  //#endregion
4813
5013
  //#region src/middleware/cache.ts
4814
5014
  /**
@@ -4852,10 +5052,14 @@ function createCacheBreakpointMiddleware() {
4852
5052
  }
4853
5053
  });
4854
5054
  }
4855
-
4856
5055
  //#endregion
4857
5056
  //#region src/agent.ts
4858
5057
  const BASE_PROMPT = `In order to complete the objective that the user asks of you, you have access to a number of standard tools.`;
5058
+ const BUILTIN_TOOL_NAMES = new Set([
5059
+ ...FILESYSTEM_TOOL_NAMES,
5060
+ "task",
5061
+ "write_todos"
5062
+ ]);
4859
5063
  /**
4860
5064
  * Detect whether a model is an Anthropic model.
4861
5065
  * Used to gate Anthropic-specific prompt caching optimizations (cache_control breakpoints).
@@ -4901,6 +5105,8 @@ function isAnthropicModel(model) {
4901
5105
  */
4902
5106
  function createDeepAgent(params = {}) {
4903
5107
  const { model = "claude-sonnet-4-5-20250929", tools = [], systemPrompt, middleware: customMiddleware = [], subagents = [], responseFormat, contextSchema, checkpointer, store, backend, interruptOn, name, memory, skills } = params;
5108
+ const collidingTools = tools.map((t) => t.name).filter((n) => typeof n === "string" && BUILTIN_TOOL_NAMES.has(n));
5109
+ 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
5110
  const anthropicModel = isAnthropicModel(model);
4905
5111
  const finalSystemPrompt = new langchain.SystemMessage({ content: systemPrompt ? typeof systemPrompt === "string" ? [{
4906
5112
  type: "text",
@@ -4982,10 +5188,6 @@ function createDeepAgent(params = {}) {
4982
5188
  model,
4983
5189
  backend: filesystemBackend
4984
5190
  }),
4985
- (0, langchain.anthropicPromptCachingMiddleware)({
4986
- unsupportedModelBehavior: "ignore",
4987
- minMessagesToCache: 1
4988
- }),
4989
5191
  createPatchToolCallsMiddleware()
4990
5192
  ];
4991
5193
  /**
@@ -5008,11 +5210,17 @@ function createDeepAgent(params = {}) {
5008
5210
  createSubAgentMiddleware({
5009
5211
  defaultModel: model,
5010
5212
  defaultTools: tools,
5011
- defaultMiddleware: [...subagentMiddleware, ...anthropicModel ? [createCacheBreakpointMiddleware()] : []],
5213
+ defaultMiddleware: [...subagentMiddleware, ...anthropicModel ? [(0, langchain.anthropicPromptCachingMiddleware)({
5214
+ unsupportedModelBehavior: "ignore",
5215
+ minMessagesToCache: 1
5216
+ }), createCacheBreakpointMiddleware()] : []],
5012
5217
  generalPurposeMiddleware: [
5013
5218
  ...subagentMiddleware,
5014
5219
  ...skillsMiddlewareArray,
5015
- ...anthropicModel ? [createCacheBreakpointMiddleware()] : []
5220
+ ...anthropicModel ? [(0, langchain.anthropicPromptCachingMiddleware)({
5221
+ unsupportedModelBehavior: "ignore",
5222
+ minMessagesToCache: 1
5223
+ }), createCacheBreakpointMiddleware()] : []
5016
5224
  ],
5017
5225
  defaultInterruptOn: interruptOn,
5018
5226
  subagents: processedSubagents,
@@ -5022,17 +5230,16 @@ function createDeepAgent(params = {}) {
5022
5230
  model,
5023
5231
  backend: filesystemBackend
5024
5232
  }),
5025
- (0, langchain.anthropicPromptCachingMiddleware)({
5026
- unsupportedModelBehavior: "ignore",
5027
- minMessagesToCache: 1
5028
- }),
5029
5233
  createPatchToolCallsMiddleware()
5030
5234
  ],
5031
5235
  ...skillsMiddlewareArray,
5032
- ...anthropicModel ? [createCacheBreakpointMiddleware()] : [],
5236
+ ...customMiddleware,
5237
+ ...anthropicModel ? [(0, langchain.anthropicPromptCachingMiddleware)({
5238
+ unsupportedModelBehavior: "ignore",
5239
+ minMessagesToCache: 1
5240
+ }), createCacheBreakpointMiddleware()] : [],
5033
5241
  ...memoryMiddlewareArray,
5034
- ...interruptOn ? [(0, langchain.humanInTheLoopMiddleware)({ interruptOn })] : [],
5035
- ...customMiddleware
5242
+ ...interruptOn ? [(0, langchain.humanInTheLoopMiddleware)({ interruptOn })] : []
5036
5243
  ],
5037
5244
  ...responseFormat != null && { responseFormat },
5038
5245
  contextSchema,
@@ -5044,7 +5251,6 @@ function createDeepAgent(params = {}) {
5044
5251
  metadata: { ls_integration: "deepagents" }
5045
5252
  });
5046
5253
  }
5047
-
5048
5254
  //#endregion
5049
5255
  //#region src/config.ts
5050
5256
  /**
@@ -5138,7 +5344,6 @@ function createSettings(options = {}) {
5138
5344
  }
5139
5345
  };
5140
5346
  }
5141
-
5142
5347
  //#endregion
5143
5348
  //#region src/middleware/agent-memory.ts
5144
5349
  /**
@@ -5365,38 +5570,6 @@ function createAgentMemoryMiddleware(options) {
5365
5570
  }
5366
5571
  });
5367
5572
  }
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
5573
  const MAX_SKILL_DESCRIPTION_LENGTH$1 = 1024;
5401
5574
  /** Pattern for validating skill names per Agent Skills spec */
5402
5575
  const SKILL_NAME_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
@@ -5441,7 +5614,7 @@ function validateSkillName(name, directoryName) {
5441
5614
  valid: false,
5442
5615
  error: "name is required"
5443
5616
  };
5444
- if (name.length > MAX_SKILL_NAME_LENGTH$1) return {
5617
+ if (name.length > 64) return {
5445
5618
  valid: false,
5446
5619
  error: "name exceeds 64 characters"
5447
5620
  };
@@ -5481,7 +5654,7 @@ function parseFrontmatter(content) {
5481
5654
  function parseSkillMetadata(skillMdPath, source) {
5482
5655
  try {
5483
5656
  const stats = node_fs.default.statSync(skillMdPath);
5484
- if (stats.size > MAX_SKILL_FILE_SIZE$1) {
5657
+ if (stats.size > 10485760) {
5485
5658
  console.warn(`Skipping ${skillMdPath}: file too large (${stats.size} bytes)`);
5486
5659
  return null;
5487
5660
  }
@@ -5500,7 +5673,7 @@ function parseSkillMetadata(skillMdPath, source) {
5500
5673
  const validation = validateSkillName(String(name), directoryName);
5501
5674
  if (!validation.valid) console.warn(`Skill '${name}' in ${skillMdPath} does not follow Agent Skills spec: ${validation.error}. Consider renaming to be spec-compliant.`);
5502
5675
  let descriptionStr = String(description);
5503
- if (descriptionStr.length > MAX_SKILL_DESCRIPTION_LENGTH$1) {
5676
+ if (descriptionStr.length > 1024) {
5504
5677
  console.warn(`Description exceeds ${MAX_SKILL_DESCRIPTION_LENGTH$1} chars in ${skillMdPath}, truncating`);
5505
5678
  descriptionStr = descriptionStr.slice(0, MAX_SKILL_DESCRIPTION_LENGTH$1);
5506
5679
  }
@@ -5588,14 +5761,15 @@ function listSkills(options) {
5588
5761
  }
5589
5762
  return Array.from(allSkills.values());
5590
5763
  }
5591
-
5592
5764
  //#endregion
5593
5765
  exports.BaseSandbox = BaseSandbox;
5594
5766
  exports.CompositeBackend = CompositeBackend;
5767
+ exports.ConfigurationError = ConfigurationError;
5595
5768
  exports.DEFAULT_GENERAL_PURPOSE_DESCRIPTION = DEFAULT_GENERAL_PURPOSE_DESCRIPTION;
5596
5769
  exports.DEFAULT_SUBAGENT_PROMPT = DEFAULT_SUBAGENT_PROMPT;
5597
5770
  exports.FilesystemBackend = FilesystemBackend;
5598
5771
  exports.GENERAL_PURPOSE_SUBAGENT = GENERAL_PURPOSE_SUBAGENT;
5772
+ exports.LangSmithSandbox = LangSmithSandbox;
5599
5773
  exports.LocalShellBackend = LocalShellBackend;
5600
5774
  exports.MAX_SKILL_DESCRIPTION_LENGTH = MAX_SKILL_DESCRIPTION_LENGTH;
5601
5775
  exports.MAX_SKILL_FILE_SIZE = MAX_SKILL_FILE_SIZE;
@@ -5619,4 +5793,5 @@ exports.findProjectRoot = findProjectRoot;
5619
5793
  exports.isSandboxBackend = isSandboxBackend;
5620
5794
  exports.listSkills = listSkills;
5621
5795
  exports.parseSkillMetadata = parseSkillMetadata;
5796
+
5622
5797
  //# sourceMappingURL=index.cjs.map