deepagents 1.6.3 → 1.7.1

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/README.md CHANGED
@@ -1,25 +1,39 @@
1
- # 🧠🤖 Deep Agents
2
-
3
- Using an LLM to call tools in a loop is the simplest form of an agent.
4
- This architecture, however, can yield agents that are "shallow" and fail to plan and act over longer, more complex tasks.
1
+ <div align="center">
2
+ <a href="https://docs.langchain.com/oss/python/deepagents/overview#deep-agents-overview">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-dark.svg">
5
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-light.svg">
6
+ <img alt="Deep Agents Logo" src="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-dark.svg" width="80%">
7
+ </picture>
8
+ </a>
9
+ </div>
10
+
11
+ <div align="center">
12
+ <h3>The batteries-included agent harness.</h3>
13
+ </div>
14
+
15
+ <div align="center">
16
+ <a href="https://www.npmjs.com/package/deepagents"><img src="https://img.shields.io/npm/v/deepagents.svg" alt="npm version"></a>
17
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
18
+ <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-5.0+-blue.svg" alt="TypeScript"></a>
19
+ <a href="https://x.com/LangChain_JS" target="_blank"><img src="https://img.shields.io/twitter/url/https/twitter.com/LangChain_JS.svg?style=social&label=Follow%20%40LangChain_JS" alt="Twitter / X"></a>
20
+ </div>
21
+
22
+ Using an LLM to call tools in a loop is the simplest form of an agent. This architecture, however, can yield agents that are "shallow" and fail to plan and act over longer, more complex tasks.
5
23
 
6
24
  Applications like "Deep Research", "Manus", and "Claude Code" have gotten around this limitation by implementing a combination of four things:
7
25
  a **planning tool**, **sub agents**, access to a **file system**, and a **detailed prompt**.
8
26
 
9
- > 💡 **Tip:** Looking for the Python version of this package? See [langchain-ai/deepagents](https://github.com/langchain-ai/deepagents)
10
-
11
- ![Deep Agents](https://blog.langchain.com/content/images/2025/07/Screenshot-2025-07-30-at-9.08.32-AM.png)
12
-
13
27
  `deepagents` is a TypeScript package that implements these in a general purpose way so that you can easily create a Deep Agent for your application.
14
28
 
15
- **Acknowledgements: This project was primarily inspired by Claude Code, and initially was largely an attempt to see what made Claude Code general purpose, and make it even more so.**
29
+ > 💡 **Tip:** Looking for the Python version of this package? See [langchain-ai/deepagents](https://github.com/langchain-ai/deepagents)
16
30
 
17
- [![npm version](https://img.shields.io/npm/v/deepagents.svg)](https://www.npmjs.com/package/deepagents)
18
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
19
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
31
+ <div align="center">
20
32
 
21
33
  [Documentation](https://docs.langchain.com/oss/javascript/deepagents/overview) | [Examples](./examples) | [Report Bug](https://github.com/langchain-ai/deepagentsjs/issues) | [Request Feature](https://github.com/langchain-ai/deepagentsjs/issues)
22
34
 
35
+ </div>
36
+
23
37
  ## 📖 Overview
24
38
 
25
39
  Using an LLM to call tools in a loop is the simplest form of an agent. However, this architecture can yield agents that are "shallow" and fail to plan and act over longer, more complex tasks.
@@ -316,6 +330,7 @@ interface SubAgent {
316
330
  model?: LanguageModelLike | string;
317
331
  middleware?: AgentMiddleware[];
318
332
  interruptOn?: Record<string, boolean | InterruptOnConfig>;
333
+ skills?: string[];
319
334
  }
320
335
  ```
321
336
 
@@ -328,6 +343,37 @@ interface SubAgent {
328
343
  - **model**: Optional model name or model instance.
329
344
  - **middleware**: Additional middleware to attach to the subagent. See [here](https://docs.langchain.com/oss/typescript/langchain/middleware) for an introduction into middleware and how it works with createAgent.
330
345
  - **interruptOn**: A custom interrupt config that specifies human-in-the-loop interactions for your tools.
346
+ - **skills**: Skill source paths for the subagent (e.g., `["/skills/research/"]`). See skills inheritance below.
347
+
348
+ #### Skills Inheritance
349
+
350
+ When you configure `skills` on the main agent via `createDeepAgent`, the behavior differs between subagent types:
351
+
352
+ - **General-purpose subagent**: Automatically inherits skills from the main agent. This subagent has access to all the same skills as the main agent.
353
+ - **Custom subagents**: Do NOT inherit skills from the main agent by default. If you want a custom subagent to have access to skills, you must explicitly define the `skills` property on that subagent.
354
+
355
+ ```typescript
356
+ const agent = createDeepAgent({
357
+ model: "claude-sonnet-4-20250514",
358
+ skills: ["/skills/"], // Main agent and general-purpose subagent get these skills
359
+ subagents: [
360
+ {
361
+ name: "researcher",
362
+ description: "Research assistant",
363
+ systemPrompt: "You are a researcher.",
364
+ // This subagent will NOT have access to /skills/ from the main agent
365
+ },
366
+ {
367
+ name: "coder",
368
+ description: "Coding assistant",
369
+ systemPrompt: "You are a coder.",
370
+ skills: ["/skills/coding/"], // This subagent has its own skills
371
+ },
372
+ ],
373
+ });
374
+ ```
375
+
376
+ This design ensures context isolation - custom subagents only have access to the skills they explicitly need, preventing unintended skill leakage between specialized agents.
331
377
 
332
378
  #### Using SubAgent
333
379
 
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
- //#region rolldown:runtime
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -26,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
27
 
27
28
  //#endregion
28
29
  let langchain = require("langchain");
30
+ let _langchain_core_runnables = require("@langchain/core/runnables");
29
31
  let _langchain_langgraph = require("@langchain/langgraph");
30
32
  let zod_v4 = require("zod/v4");
31
33
  let micromatch = require("micromatch");
@@ -36,7 +38,7 @@ let zod = require("zod");
36
38
  let yaml = require("yaml");
37
39
  yaml = __toESM(yaml);
38
40
  require("uuid");
39
- require("@langchain/openai");
41
+ require("langchain/chat_models/universal");
40
42
  let node_fs_promises = require("node:fs/promises");
41
43
  node_fs_promises = __toESM(node_fs_promises);
42
44
  let node_fs = require("node:fs");
@@ -59,6 +61,55 @@ node_os = __toESM(node_os);
59
61
  function isSandboxBackend(backend) {
60
62
  return typeof backend.execute === "function" && typeof backend.id === "string";
61
63
  }
64
+ const SANDBOX_ERROR_SYMBOL = Symbol.for("sandbox.error");
65
+ /**
66
+ * Custom error class for sandbox operations.
67
+ *
68
+ * @param message - Human-readable error description
69
+ * @param code - Structured error code for programmatic handling
70
+ * @returns SandboxError with message and code
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * try {
75
+ * await sandbox.execute("some command");
76
+ * } catch (error) {
77
+ * if (error instanceof SandboxError) {
78
+ * switch (error.code) {
79
+ * case "NOT_INITIALIZED":
80
+ * await sandbox.initialize();
81
+ * break;
82
+ * case "COMMAND_TIMEOUT":
83
+ * console.error("Command took too long");
84
+ * break;
85
+ * default:
86
+ * throw error;
87
+ * }
88
+ * }
89
+ * }
90
+ * ```
91
+ */
92
+ var SandboxError = class SandboxError extends Error {
93
+ /** Symbol for identifying sandbox error instances */
94
+ [SANDBOX_ERROR_SYMBOL] = true;
95
+ /** Error name for instanceof checks and logging */
96
+ name = "SandboxError";
97
+ /**
98
+ * Creates a new SandboxError.
99
+ *
100
+ * @param message - Human-readable error description
101
+ * @param code - Structured error code for programmatic handling
102
+ */
103
+ constructor(message, code, cause) {
104
+ super(message);
105
+ this.code = code;
106
+ this.cause = cause;
107
+ Object.setPrototypeOf(this, SandboxError.prototype);
108
+ }
109
+ static isInstance(error) {
110
+ return typeof error === "object" && error !== null && error[SANDBOX_ERROR_SYMBOL] === true;
111
+ }
112
+ };
62
113
 
63
114
  //#endregion
64
115
  //#region src/backends/utils.ts
@@ -229,8 +280,8 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
229
280
  * validatePath("C:\\Users\\file") // Throws: Windows absolute paths not supported
230
281
  * ```
231
282
  */
232
- function validatePath(path) {
233
- const pathStr = path || "/";
283
+ function validatePath(path$4) {
284
+ const pathStr = path$4 || "/";
234
285
  if (!pathStr || pathStr.trim() === "") throw new Error("Path cannot be empty");
235
286
  let normalized = pathStr.startsWith("/") ? pathStr : "/" + pathStr;
236
287
  if (!normalized.endsWith("/")) normalized += "/";
@@ -252,10 +303,10 @@ function validatePath(path) {
252
303
  * // Returns: "/test.py\n/src/main.py" (sorted by modified_at)
253
304
  * ```
254
305
  */
255
- function globSearchFiles(files, pattern, path = "/") {
306
+ function globSearchFiles(files, pattern, path$6 = "/") {
256
307
  let normalizedPath;
257
308
  try {
258
- normalizedPath = validatePath(path);
309
+ normalizedPath = validatePath(path$6);
259
310
  } catch {
260
311
  return "No files found";
261
312
  }
@@ -281,20 +332,16 @@ function globSearchFiles(files, pattern, path = "/") {
281
332
  /**
282
333
  * Return structured grep matches from an in-memory files mapping.
283
334
  *
284
- * Returns a list of GrepMatch on success, or a string for invalid inputs
285
- * (e.g., invalid regex). We deliberately do not raise here to keep backends
286
- * non-throwing in tool contexts and preserve user-facing error messages.
335
+ * Performs literal text search (not regex).
336
+ *
337
+ * Returns a list of GrepMatch on success, or a string for invalid inputs.
338
+ * We deliberately do not raise here to keep backends non-throwing in tool
339
+ * contexts and preserve user-facing error messages.
287
340
  */
288
- function grepMatchesFromFiles(files, pattern, path = null, glob = null) {
289
- let regex;
290
- try {
291
- regex = new RegExp(pattern);
292
- } catch (e) {
293
- return `Invalid regex pattern: ${e.message}`;
294
- }
341
+ function grepMatchesFromFiles(files, pattern, path$8 = null, glob = null) {
295
342
  let normalizedPath;
296
343
  try {
297
- normalizedPath = validatePath(path);
344
+ normalizedPath = validatePath(path$8);
298
345
  } catch {
299
346
  return [];
300
347
  }
@@ -307,7 +354,7 @@ function grepMatchesFromFiles(files, pattern, path = null, glob = null) {
307
354
  for (const [filePath, fileData] of Object.entries(filtered)) for (let i = 0; i < fileData.content.length; i++) {
308
355
  const line = fileData.content[i];
309
356
  const lineNum = i + 1;
310
- if (regex.test(line)) matches.push({
357
+ if (line.includes(pattern)) matches.push({
311
358
  path: filePath,
312
359
  line: lineNum,
313
360
  text: line
@@ -714,11 +761,13 @@ Examples:
714
761
  const GREP_TOOL_DESCRIPTION = `Search for a text pattern across files.
715
762
 
716
763
  Searches for literal text (not regex) and returns matching files or content based on output_mode.
764
+ Special characters like parentheses, brackets, pipes, etc. are treated as literal characters, not regex operators.
717
765
 
718
766
  Examples:
719
767
  - Search all files: \`grep(pattern="TODO")\`
720
768
  - Search Python files only: \`grep(pattern="import", glob="*.py")\`
721
- - Show matching lines: \`grep(pattern="error", output_mode="content")\``;
769
+ - Show matching lines: \`grep(pattern="error", output_mode="content")\`
770
+ - Search for code with special chars: \`grep(pattern="def __init__(self):")\``;
722
771
  const EXECUTE_TOOL_DESCRIPTION = `Executes a shell command in an isolated sandbox environment.
723
772
 
724
773
  Usage:
@@ -1003,14 +1052,13 @@ function createFilesystemMiddleware(options = {}) {
1003
1052
  }));
1004
1053
  let tools = request.tools;
1005
1054
  if (!supportsExecution) tools = tools.filter((t) => t.name !== "execute");
1006
- let systemPrompt = baseSystemPrompt;
1007
- if (supportsExecution) systemPrompt = `${systemPrompt}\n\n${EXECUTION_SYSTEM_PROMPT}`;
1008
- const currentSystemPrompt = request.systemPrompt || "";
1009
- const newSystemPrompt = currentSystemPrompt ? `${currentSystemPrompt}\n\n${systemPrompt}` : systemPrompt;
1055
+ let filesystemPrompt = baseSystemPrompt;
1056
+ if (supportsExecution) filesystemPrompt = `${filesystemPrompt}\n\n${EXECUTION_SYSTEM_PROMPT}`;
1057
+ const newSystemMessage = request.systemMessage.concat(filesystemPrompt);
1010
1058
  return handler({
1011
1059
  ...request,
1012
1060
  tools,
1013
- systemPrompt: newSystemPrompt
1061
+ systemMessage: newSystemMessage
1014
1062
  });
1015
1063
  },
1016
1064
  wrapToolCall: async (request, handler) => {
@@ -1080,12 +1128,34 @@ function createFilesystemMiddleware(options = {}) {
1080
1128
 
1081
1129
  //#endregion
1082
1130
  //#region src/middleware/subagents.ts
1131
+ /**
1132
+ * Default system prompt for subagents.
1133
+ * Provides a minimal base prompt that can be extended by specific subagent configurations.
1134
+ */
1083
1135
  const DEFAULT_SUBAGENT_PROMPT = "In order to complete the objective that the user asks of you, you have access to a number of standard tools.";
1136
+ /**
1137
+ * State keys that are excluded when passing state to subagents and when returning
1138
+ * updates from subagents.
1139
+ *
1140
+ * When returning updates:
1141
+ * 1. The messages key is handled explicitly to ensure only the final message is included
1142
+ * 2. The todos and structuredResponse keys are excluded as they do not have a defined reducer
1143
+ * and no clear meaning for returning them from a subagent to the main agent.
1144
+ * 3. The skillsMetadata and memoryContents keys are automatically excluded from subagent output
1145
+ * to prevent parent state from leaking to child agents. Each agent loads its own skills/memory
1146
+ * independently based on its middleware configuration.
1147
+ */
1084
1148
  const EXCLUDED_STATE_KEYS = [
1085
1149
  "messages",
1086
1150
  "todos",
1087
- "structuredResponse"
1151
+ "structuredResponse",
1152
+ "skillsMetadata",
1153
+ "memoryContents"
1088
1154
  ];
1155
+ /**
1156
+ * Default description for the general-purpose subagent.
1157
+ * This description is shown to the model when selecting which subagent to use.
1158
+ */
1089
1159
  const DEFAULT_GENERAL_PURPOSE_DESCRIPTION = "General-purpose agent for researching complex questions, searching for files and content, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. This agent has access to all tools as the main agent.";
1090
1160
  function getTaskToolDescription(subagentDescriptions) {
1091
1161
  return `
@@ -1200,6 +1270,19 @@ assistant: "I'm going to use the Task tool to launch with the greeting-responder
1200
1270
  </example>
1201
1271
  `.trim();
1202
1272
  }
1273
+ /**
1274
+ * System prompt section that explains how to use the task tool for spawning subagents.
1275
+ *
1276
+ * This prompt is automatically appended to the main agent's system prompt when
1277
+ * using `createSubAgentMiddleware`. It provides guidance on:
1278
+ * - When to use the task tool
1279
+ * - Subagent lifecycle (spawn → run → return → reconcile)
1280
+ * - When NOT to use the task tool
1281
+ * - Best practices for parallel task execution
1282
+ *
1283
+ * You can provide a custom `systemPrompt` to `createSubAgentMiddleware` to override
1284
+ * or extend this default.
1285
+ */
1203
1286
  const TASK_SYSTEM_PROMPT = `## \`task\` (subagent spawner)
1204
1287
 
1205
1288
  You have access to a \`task\` tool to launch short-lived subagents that handle isolated tasks. These agents are ephemeral — they live only for the duration of the task and return a single result.
@@ -1228,6 +1311,48 @@ When NOT to use the task tool:
1228
1311
  - Remember to use the \`task\` tool to silo independent tasks within a multi-part objective.
1229
1312
  - You should use the \`task\` tool whenever you have a complex task that will take multiple steps, and is independent from other tasks that the agent needs to complete. These agents are highly competent and efficient.`;
1230
1313
  /**
1314
+ * Base specification for the general-purpose subagent.
1315
+ *
1316
+ * This constant provides the default configuration for the general-purpose subagent
1317
+ * that is automatically included when `generalPurposeAgent: true` (the default).
1318
+ *
1319
+ * The general-purpose subagent:
1320
+ * - Has access to all tools from the main agent
1321
+ * - Inherits skills from the main agent (when skills are configured)
1322
+ * - Uses the same model as the main agent (by default)
1323
+ * - Is ideal for delegating complex, multi-step tasks
1324
+ *
1325
+ * You can spread this constant and override specific properties when creating
1326
+ * custom subagents that should behave similarly to the general-purpose agent:
1327
+ *
1328
+ * @example
1329
+ * ```typescript
1330
+ * import { GENERAL_PURPOSE_SUBAGENT, createDeepAgent } from "@anthropic/deepagents";
1331
+ *
1332
+ * // Use as-is (automatically included with generalPurposeAgent: true)
1333
+ * const agent = createDeepAgent({ model: "claude-sonnet-4-5-20250929" });
1334
+ *
1335
+ * // Or create a custom variant with different tools
1336
+ * const customGP: SubAgent = {
1337
+ * ...GENERAL_PURPOSE_SUBAGENT,
1338
+ * name: "research-gp",
1339
+ * tools: [webSearchTool, readFileTool],
1340
+ * };
1341
+ *
1342
+ * const agent = createDeepAgent({
1343
+ * model: "claude-sonnet-4-5-20250929",
1344
+ * subagents: [customGP],
1345
+ * // Disable the default general-purpose agent since we're providing our own
1346
+ * // (handled automatically when using createSubAgentMiddleware directly)
1347
+ * });
1348
+ * ```
1349
+ */
1350
+ const GENERAL_PURPOSE_SUBAGENT = {
1351
+ name: "general-purpose",
1352
+ description: DEFAULT_GENERAL_PURPOSE_DESCRIPTION,
1353
+ systemPrompt: DEFAULT_SUBAGENT_PROMPT
1354
+ };
1355
+ /**
1231
1356
  * Filter state to exclude certain keys when passing to subagents
1232
1357
  */
1233
1358
  function filterStateForSubagent(state) {
@@ -1255,12 +1380,13 @@ function returnCommandWithStateUpdate(result, toolCallId) {
1255
1380
  * Create subagent instances from specifications
1256
1381
  */
1257
1382
  function getSubagents(options) {
1258
- const { defaultModel, defaultTools, defaultMiddleware, defaultInterruptOn, subagents, generalPurposeAgent } = options;
1383
+ const { defaultModel, defaultTools, defaultMiddleware, generalPurposeMiddleware: gpMiddleware, defaultInterruptOn, subagents, generalPurposeAgent } = options;
1259
1384
  const defaultSubagentMiddleware = defaultMiddleware || [];
1385
+ const generalPurposeMiddlewareBase = gpMiddleware || defaultSubagentMiddleware;
1260
1386
  const agents = {};
1261
1387
  const subagentDescriptions = [];
1262
1388
  if (generalPurposeAgent) {
1263
- const generalPurposeMiddleware = [...defaultSubagentMiddleware];
1389
+ const generalPurposeMiddleware = [...generalPurposeMiddlewareBase];
1264
1390
  if (defaultInterruptOn) generalPurposeMiddleware.push((0, langchain.humanInTheLoopMiddleware)({ interruptOn: defaultInterruptOn }));
1265
1391
  agents["general-purpose"] = (0, langchain.createAgent)({
1266
1392
  model: defaultModel,
@@ -1294,11 +1420,12 @@ function getSubagents(options) {
1294
1420
  * Create the task tool for invoking subagents
1295
1421
  */
1296
1422
  function createTaskTool(options) {
1297
- const { defaultModel, defaultTools, defaultMiddleware, defaultInterruptOn, subagents, generalPurposeAgent, taskDescription } = options;
1423
+ const { defaultModel, defaultTools, defaultMiddleware, generalPurposeMiddleware, defaultInterruptOn, subagents, generalPurposeAgent, taskDescription } = options;
1298
1424
  const { agents: subagentGraphs, descriptions: subagentDescriptions } = getSubagents({
1299
1425
  defaultModel,
1300
1426
  defaultTools,
1301
1427
  defaultMiddleware,
1428
+ generalPurposeMiddleware,
1302
1429
  defaultInterruptOn,
1303
1430
  subagents,
1304
1431
  generalPurposeAgent
@@ -1328,13 +1455,14 @@ function createTaskTool(options) {
1328
1455
  * Create subagent middleware with task tool
1329
1456
  */
1330
1457
  function createSubAgentMiddleware(options) {
1331
- const { defaultModel, defaultTools = [], defaultMiddleware = null, defaultInterruptOn = null, subagents = [], systemPrompt = TASK_SYSTEM_PROMPT, generalPurposeAgent = true, taskDescription = null } = options;
1458
+ const { defaultModel, defaultTools = [], defaultMiddleware = null, generalPurposeMiddleware = null, defaultInterruptOn = null, subagents = [], systemPrompt = TASK_SYSTEM_PROMPT, generalPurposeAgent = true, taskDescription = null } = options;
1332
1459
  return (0, langchain.createMiddleware)({
1333
1460
  name: "subAgentMiddleware",
1334
1461
  tools: [createTaskTool({
1335
1462
  defaultModel,
1336
1463
  defaultTools,
1337
1464
  defaultMiddleware,
1465
+ generalPurposeMiddleware,
1338
1466
  defaultInterruptOn,
1339
1467
  subagents,
1340
1468
  generalPurposeAgent,
@@ -2398,7 +2526,7 @@ var StoreBackend = class {
2398
2526
  * Security and search upgrades:
2399
2527
  * - Secure path resolution with root containment when in virtual_mode (sandboxed to cwd)
2400
2528
  * - Prevent symlink-following on file I/O using O_NOFOLLOW when available
2401
- * - Ripgrep-powered grep with JSON parsing, plus regex fallback
2529
+ * - Ripgrep-powered grep with literal (fixed-string) search, plus substring fallback
2402
2530
  * and optional glob include filtering, while preserving virtual path behavior
2403
2531
  */
2404
2532
  const SUPPORTS_NOFOLLOW = node_fs.default.constants.O_NOFOLLOW !== void 0;
@@ -2647,14 +2775,16 @@ var FilesystemBackend = class {
2647
2775
  }
2648
2776
  }
2649
2777
  /**
2650
- * Structured search results or error string for invalid input.
2778
+ * Search for a literal text pattern in files.
2779
+ *
2780
+ * Uses ripgrep if available, falling back to substring search.
2781
+ *
2782
+ * @param pattern - Literal string to search for (NOT regex).
2783
+ * @param dirPath - Directory or file path to search in. Defaults to current directory.
2784
+ * @param glob - Optional glob pattern to filter which files to search.
2785
+ * @returns List of GrepMatch dicts containing path, line number, and matched text.
2651
2786
  */
2652
2787
  async grepRaw(pattern, dirPath = "/", glob = null) {
2653
- try {
2654
- new RegExp(pattern);
2655
- } catch (e) {
2656
- return `Invalid regex pattern: ${e.message}`;
2657
- }
2658
2788
  let baseFull;
2659
2789
  try {
2660
2790
  baseFull = this.resolvePath(dirPath || ".");
@@ -2667,7 +2797,7 @@ var FilesystemBackend = class {
2667
2797
  return [];
2668
2798
  }
2669
2799
  let results = await this.ripgrepSearch(pattern, baseFull, glob);
2670
- if (results === null) results = await this.pythonSearch(pattern, baseFull, glob);
2800
+ if (results === null) results = await this.literalSearch(pattern, baseFull, glob);
2671
2801
  const matches = [];
2672
2802
  for (const [fpath, items] of Object.entries(results)) for (const [lineNum, lineText] of items) matches.push({
2673
2803
  path: fpath,
@@ -2677,12 +2807,17 @@ var FilesystemBackend = class {
2677
2807
  return matches;
2678
2808
  }
2679
2809
  /**
2680
- * Try to use ripgrep for fast searching.
2681
- * Returns null if ripgrep is not available or fails.
2810
+ * Search using ripgrep with fixed-string (literal) mode.
2811
+ *
2812
+ * @param pattern - Literal string to search for (unescaped).
2813
+ * @param baseFull - Resolved base path to search in.
2814
+ * @param includeGlob - Optional glob pattern to filter files.
2815
+ * @returns Dict mapping file paths to list of (line_number, line_text) tuples.
2816
+ * Returns null if ripgrep is unavailable or times out.
2682
2817
  */
2683
2818
  async ripgrepSearch(pattern, baseFull, includeGlob) {
2684
2819
  return new Promise((resolve) => {
2685
- const args = ["--json"];
2820
+ const args = ["--json", "-F"];
2686
2821
  if (includeGlob) args.push("--glob", includeGlob);
2687
2822
  args.push("--", pattern, baseFull);
2688
2823
  const proc = (0, node_child_process.spawn)("rg", args, { timeout: 3e4 });
@@ -2731,15 +2866,16 @@ var FilesystemBackend = class {
2731
2866
  });
2732
2867
  }
2733
2868
  /**
2734
- * Fallback regex search implementation.
2869
+ * Fallback search using literal substring matching when ripgrep is unavailable.
2870
+ *
2871
+ * Recursively searches files, respecting maxFileSizeBytes limit.
2872
+ *
2873
+ * @param pattern - Literal string to search for.
2874
+ * @param baseFull - Resolved base path to search in.
2875
+ * @param includeGlob - Optional glob pattern to filter files by name.
2876
+ * @returns Dict mapping file paths to list of (line_number, line_text) tuples.
2735
2877
  */
2736
- async pythonSearch(pattern, baseFull, includeGlob) {
2737
- let regex;
2738
- try {
2739
- regex = new RegExp(pattern);
2740
- } catch {
2741
- return {};
2742
- }
2878
+ async literalSearch(pattern, baseFull, includeGlob) {
2743
2879
  const results = {};
2744
2880
  const files = await (0, fast_glob.default)("**/*", {
2745
2881
  cwd: (await node_fs_promises.default.stat(baseFull)).isDirectory() ? baseFull : node_path.default.dirname(baseFull),
@@ -2753,7 +2889,7 @@ var FilesystemBackend = class {
2753
2889
  const lines = (await node_fs_promises.default.readFile(fp, "utf-8")).split("\n");
2754
2890
  for (let i = 0; i < lines.length; i++) {
2755
2891
  const line = lines[i];
2756
- if (regex.test(line)) {
2892
+ if (line.includes(pattern)) {
2757
2893
  let virtPath;
2758
2894
  if (this.virtualMode) try {
2759
2895
  const relative = node_path.default.relative(this.cwd, fp);
@@ -3317,7 +3453,11 @@ console.log(count);
3317
3453
  "`;
3318
3454
  }
3319
3455
  /**
3320
- * Node.js command template for grep operations.
3456
+ * Node.js command template for grep operations with literal (fixed-string) search.
3457
+ *
3458
+ * @param pattern - Literal string to search for (NOT regex).
3459
+ * @param searchPath - Base path to search in.
3460
+ * @param globPattern - Optional glob pattern to filter files.
3321
3461
  */
3322
3462
  function buildGrepCommand(pattern, searchPath, globPattern) {
3323
3463
  const patternB64 = btoa(pattern);
@@ -3331,14 +3471,6 @@ const pattern = atob('${patternB64}');
3331
3471
  const searchPath = atob('${pathB64}');
3332
3472
  const globPattern = ${globPattern ? `atob('${globB64}')` : "null"};
3333
3473
 
3334
- let regex;
3335
- try {
3336
- regex = new RegExp(pattern);
3337
- } catch (e) {
3338
- console.error('Invalid regex: ' + e.message);
3339
- process.exit(1);
3340
- }
3341
-
3342
3474
  function globMatch(filePath, pattern) {
3343
3475
  if (!pattern) return true;
3344
3476
  const regexPattern = pattern
@@ -3363,7 +3495,8 @@ function walkDir(dir, results) {
3363
3495
  const content = fs.readFileSync(fullPath, 'utf-8');
3364
3496
  const lines = content.split('\\n');
3365
3497
  for (let i = 0; i < lines.length; i++) {
3366
- if (regex.test(lines[i])) {
3498
+ // Simple substring search for literal matching
3499
+ if (lines[i].includes(pattern)) {
3367
3500
  console.log(JSON.stringify({
3368
3501
  path: fullPath,
3369
3502
  line: i + 1,
@@ -3459,14 +3592,16 @@ var BaseSandbox = class {
3459
3592
  };
3460
3593
  }
3461
3594
  /**
3462
- * Structured search results or error string for invalid input.
3595
+ * Search for a literal text pattern in files.
3596
+ *
3597
+ * @param pattern - Literal string to search for (NOT regex).
3598
+ * @param path - Directory or file path to search in.
3599
+ * @param glob - Optional glob pattern to filter which files to search.
3600
+ * @returns List of GrepMatch dicts containing path, line number, and matched text.
3463
3601
  */
3464
3602
  async grepRaw(pattern, path = "/", glob = null) {
3465
3603
  const command = buildGrepCommand(pattern, path, glob);
3466
3604
  const result = await this.execute(command);
3467
- if (result.exitCode === 1) {
3468
- if (result.output.includes("Invalid regex:")) return result.output.trim();
3469
- }
3470
3605
  const matches = [];
3471
3606
  const lines = result.output.trim().split("\n").filter(Boolean);
3472
3607
  for (const line of lines) try {
@@ -3595,6 +3730,51 @@ function createDeepAgent(params = {}) {
3595
3730
  sources: memory
3596
3731
  })] : [];
3597
3732
  /**
3733
+ * Process subagents to add SkillsMiddleware for those with their own skills.
3734
+ *
3735
+ * Custom subagents do NOT inherit skills from the main agent by default.
3736
+ * Only the general-purpose subagent inherits the main agent's skills (via defaultMiddleware).
3737
+ * If a custom subagent needs skills, it must specify its own `skills` array.
3738
+ */
3739
+ const processedSubagents = subagents.map((subagent) => {
3740
+ /**
3741
+ * CompiledSubAgent - use as-is (already has its own middleware baked in)
3742
+ */
3743
+ if (_langchain_core_runnables.Runnable.isRunnable(subagent)) return subagent;
3744
+ /**
3745
+ * SubAgent without skills - use as-is
3746
+ */
3747
+ if (!("skills" in subagent) || subagent.skills?.length === 0) return subagent;
3748
+ /**
3749
+ * SubAgent with skills - add SkillsMiddleware BEFORE user's middleware
3750
+ * Order: base middleware (via defaultMiddleware) → skills → user's middleware
3751
+ * This matches Python's ordering in create_deep_agent
3752
+ */
3753
+ const subagentSkillsMiddleware = createSkillsMiddleware({
3754
+ backend: filesystemBackend,
3755
+ sources: subagent.skills ?? []
3756
+ });
3757
+ return {
3758
+ ...subagent,
3759
+ middleware: [subagentSkillsMiddleware, ...subagent.middleware || []]
3760
+ };
3761
+ });
3762
+ /**
3763
+ * Middleware for custom subagents (does NOT include skills from main agent).
3764
+ * Custom subagents must define their own `skills` property to get skills.
3765
+ */
3766
+ const subagentMiddleware = [
3767
+ (0, langchain.todoListMiddleware)(),
3768
+ createFilesystemMiddleware({ backend: filesystemBackend }),
3769
+ (0, langchain.summarizationMiddleware)({
3770
+ model,
3771
+ trigger: { tokens: 17e4 },
3772
+ keep: { messages: 6 }
3773
+ }),
3774
+ (0, langchain.anthropicPromptCachingMiddleware)({ unsupportedModelBehavior: "ignore" }),
3775
+ createPatchToolCallsMiddleware()
3776
+ ];
3777
+ /**
3598
3778
  * Return as DeepAgent with proper DeepAgentTypeConfig
3599
3779
  * - Response: TResponse (from responseFormat parameter)
3600
3780
  * - State: undefined (state comes from middleware)
@@ -3614,20 +3794,10 @@ function createDeepAgent(params = {}) {
3614
3794
  createSubAgentMiddleware({
3615
3795
  defaultModel: model,
3616
3796
  defaultTools: tools,
3617
- defaultMiddleware: [
3618
- (0, langchain.todoListMiddleware)(),
3619
- ...skillsMiddlewareArray,
3620
- createFilesystemMiddleware({ backend: filesystemBackend }),
3621
- (0, langchain.summarizationMiddleware)({
3622
- model,
3623
- trigger: { tokens: 17e4 },
3624
- keep: { messages: 6 }
3625
- }),
3626
- (0, langchain.anthropicPromptCachingMiddleware)({ unsupportedModelBehavior: "ignore" }),
3627
- createPatchToolCallsMiddleware()
3628
- ],
3797
+ defaultMiddleware: subagentMiddleware,
3798
+ generalPurposeMiddleware: [...subagentMiddleware, ...skillsMiddlewareArray],
3629
3799
  defaultInterruptOn: interruptOn,
3630
- subagents,
3800
+ subagents: processedSubagents,
3631
3801
  generalPurposeAgent: true
3632
3802
  }),
3633
3803
  (0, langchain.summarizationMiddleware)({
@@ -4198,12 +4368,17 @@ function listSkills(options) {
4198
4368
  //#endregion
4199
4369
  exports.BaseSandbox = BaseSandbox;
4200
4370
  exports.CompositeBackend = CompositeBackend;
4371
+ exports.DEFAULT_GENERAL_PURPOSE_DESCRIPTION = DEFAULT_GENERAL_PURPOSE_DESCRIPTION;
4372
+ exports.DEFAULT_SUBAGENT_PROMPT = DEFAULT_SUBAGENT_PROMPT;
4201
4373
  exports.FilesystemBackend = FilesystemBackend;
4374
+ exports.GENERAL_PURPOSE_SUBAGENT = GENERAL_PURPOSE_SUBAGENT;
4202
4375
  exports.MAX_SKILL_DESCRIPTION_LENGTH = MAX_SKILL_DESCRIPTION_LENGTH;
4203
4376
  exports.MAX_SKILL_FILE_SIZE = MAX_SKILL_FILE_SIZE;
4204
4377
  exports.MAX_SKILL_NAME_LENGTH = MAX_SKILL_NAME_LENGTH;
4378
+ exports.SandboxError = SandboxError;
4205
4379
  exports.StateBackend = StateBackend;
4206
4380
  exports.StoreBackend = StoreBackend;
4381
+ exports.TASK_SYSTEM_PROMPT = TASK_SYSTEM_PROMPT;
4207
4382
  exports.createAgentMemoryMiddleware = createAgentMemoryMiddleware;
4208
4383
  exports.createDeepAgent = createDeepAgent;
4209
4384
  exports.createFilesystemMiddleware = createFilesystemMiddleware;