deepagents 1.6.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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=".github/images/logo-dark.svg">
5
+ <source media="(prefers-color-scheme: dark)" srcset=".github/images/logo-light.svg">
6
+ <img alt="Deep Agents Logo" src=".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");
@@ -229,8 +231,8 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
229
231
  * validatePath("C:\\Users\\file") // Throws: Windows absolute paths not supported
230
232
  * ```
231
233
  */
232
- function validatePath(path) {
233
- const pathStr = path || "/";
234
+ function validatePath(path$4) {
235
+ const pathStr = path$4 || "/";
234
236
  if (!pathStr || pathStr.trim() === "") throw new Error("Path cannot be empty");
235
237
  let normalized = pathStr.startsWith("/") ? pathStr : "/" + pathStr;
236
238
  if (!normalized.endsWith("/")) normalized += "/";
@@ -252,10 +254,10 @@ function validatePath(path) {
252
254
  * // Returns: "/test.py\n/src/main.py" (sorted by modified_at)
253
255
  * ```
254
256
  */
255
- function globSearchFiles(files, pattern, path = "/") {
257
+ function globSearchFiles(files, pattern, path$6 = "/") {
256
258
  let normalizedPath;
257
259
  try {
258
- normalizedPath = validatePath(path);
260
+ normalizedPath = validatePath(path$6);
259
261
  } catch {
260
262
  return "No files found";
261
263
  }
@@ -281,20 +283,16 @@ function globSearchFiles(files, pattern, path = "/") {
281
283
  /**
282
284
  * Return structured grep matches from an in-memory files mapping.
283
285
  *
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.
286
+ * Performs literal text search (not regex).
287
+ *
288
+ * Returns a list of GrepMatch on success, or a string for invalid inputs.
289
+ * We deliberately do not raise here to keep backends non-throwing in tool
290
+ * contexts and preserve user-facing error messages.
287
291
  */
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
- }
292
+ function grepMatchesFromFiles(files, pattern, path$8 = null, glob = null) {
295
293
  let normalizedPath;
296
294
  try {
297
- normalizedPath = validatePath(path);
295
+ normalizedPath = validatePath(path$8);
298
296
  } catch {
299
297
  return [];
300
298
  }
@@ -307,7 +305,7 @@ function grepMatchesFromFiles(files, pattern, path = null, glob = null) {
307
305
  for (const [filePath, fileData] of Object.entries(filtered)) for (let i = 0; i < fileData.content.length; i++) {
308
306
  const line = fileData.content[i];
309
307
  const lineNum = i + 1;
310
- if (regex.test(line)) matches.push({
308
+ if (line.includes(pattern)) matches.push({
311
309
  path: filePath,
312
310
  line: lineNum,
313
311
  text: line
@@ -714,11 +712,13 @@ Examples:
714
712
  const GREP_TOOL_DESCRIPTION = `Search for a text pattern across files.
715
713
 
716
714
  Searches for literal text (not regex) and returns matching files or content based on output_mode.
715
+ Special characters like parentheses, brackets, pipes, etc. are treated as literal characters, not regex operators.
717
716
 
718
717
  Examples:
719
718
  - Search all files: \`grep(pattern="TODO")\`
720
719
  - Search Python files only: \`grep(pattern="import", glob="*.py")\`
721
- - Show matching lines: \`grep(pattern="error", output_mode="content")\``;
720
+ - Show matching lines: \`grep(pattern="error", output_mode="content")\`
721
+ - Search for code with special chars: \`grep(pattern="def __init__(self):")\``;
722
722
  const EXECUTE_TOOL_DESCRIPTION = `Executes a shell command in an isolated sandbox environment.
723
723
 
724
724
  Usage:
@@ -1003,14 +1003,13 @@ function createFilesystemMiddleware(options = {}) {
1003
1003
  }));
1004
1004
  let tools = request.tools;
1005
1005
  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;
1006
+ let filesystemPrompt = baseSystemPrompt;
1007
+ if (supportsExecution) filesystemPrompt = `${filesystemPrompt}\n\n${EXECUTION_SYSTEM_PROMPT}`;
1008
+ const newSystemMessage = request.systemMessage.concat(filesystemPrompt);
1010
1009
  return handler({
1011
1010
  ...request,
1012
1011
  tools,
1013
- systemPrompt: newSystemPrompt
1012
+ systemMessage: newSystemMessage
1014
1013
  });
1015
1014
  },
1016
1015
  wrapToolCall: async (request, handler) => {
@@ -1080,12 +1079,34 @@ function createFilesystemMiddleware(options = {}) {
1080
1079
 
1081
1080
  //#endregion
1082
1081
  //#region src/middleware/subagents.ts
1082
+ /**
1083
+ * Default system prompt for subagents.
1084
+ * Provides a minimal base prompt that can be extended by specific subagent configurations.
1085
+ */
1083
1086
  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.";
1087
+ /**
1088
+ * State keys that are excluded when passing state to subagents and when returning
1089
+ * updates from subagents.
1090
+ *
1091
+ * When returning updates:
1092
+ * 1. The messages key is handled explicitly to ensure only the final message is included
1093
+ * 2. The todos and structuredResponse keys are excluded as they do not have a defined reducer
1094
+ * and no clear meaning for returning them from a subagent to the main agent.
1095
+ * 3. The skillsMetadata and memoryContents keys are automatically excluded from subagent output
1096
+ * to prevent parent state from leaking to child agents. Each agent loads its own skills/memory
1097
+ * independently based on its middleware configuration.
1098
+ */
1084
1099
  const EXCLUDED_STATE_KEYS = [
1085
1100
  "messages",
1086
1101
  "todos",
1087
- "structuredResponse"
1102
+ "structuredResponse",
1103
+ "skillsMetadata",
1104
+ "memoryContents"
1088
1105
  ];
1106
+ /**
1107
+ * Default description for the general-purpose subagent.
1108
+ * This description is shown to the model when selecting which subagent to use.
1109
+ */
1089
1110
  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
1111
  function getTaskToolDescription(subagentDescriptions) {
1091
1112
  return `
@@ -1200,6 +1221,19 @@ assistant: "I'm going to use the Task tool to launch with the greeting-responder
1200
1221
  </example>
1201
1222
  `.trim();
1202
1223
  }
1224
+ /**
1225
+ * System prompt section that explains how to use the task tool for spawning subagents.
1226
+ *
1227
+ * This prompt is automatically appended to the main agent's system prompt when
1228
+ * using `createSubAgentMiddleware`. It provides guidance on:
1229
+ * - When to use the task tool
1230
+ * - Subagent lifecycle (spawn → run → return → reconcile)
1231
+ * - When NOT to use the task tool
1232
+ * - Best practices for parallel task execution
1233
+ *
1234
+ * You can provide a custom `systemPrompt` to `createSubAgentMiddleware` to override
1235
+ * or extend this default.
1236
+ */
1203
1237
  const TASK_SYSTEM_PROMPT = `## \`task\` (subagent spawner)
1204
1238
 
1205
1239
  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 +1262,48 @@ When NOT to use the task tool:
1228
1262
  - Remember to use the \`task\` tool to silo independent tasks within a multi-part objective.
1229
1263
  - 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
1264
  /**
1265
+ * Base specification for the general-purpose subagent.
1266
+ *
1267
+ * This constant provides the default configuration for the general-purpose subagent
1268
+ * that is automatically included when `generalPurposeAgent: true` (the default).
1269
+ *
1270
+ * The general-purpose subagent:
1271
+ * - Has access to all tools from the main agent
1272
+ * - Inherits skills from the main agent (when skills are configured)
1273
+ * - Uses the same model as the main agent (by default)
1274
+ * - Is ideal for delegating complex, multi-step tasks
1275
+ *
1276
+ * You can spread this constant and override specific properties when creating
1277
+ * custom subagents that should behave similarly to the general-purpose agent:
1278
+ *
1279
+ * @example
1280
+ * ```typescript
1281
+ * import { GENERAL_PURPOSE_SUBAGENT, createDeepAgent } from "@anthropic/deepagents";
1282
+ *
1283
+ * // Use as-is (automatically included with generalPurposeAgent: true)
1284
+ * const agent = createDeepAgent({ model: "claude-sonnet-4-5-20250929" });
1285
+ *
1286
+ * // Or create a custom variant with different tools
1287
+ * const customGP: SubAgent = {
1288
+ * ...GENERAL_PURPOSE_SUBAGENT,
1289
+ * name: "research-gp",
1290
+ * tools: [webSearchTool, readFileTool],
1291
+ * };
1292
+ *
1293
+ * const agent = createDeepAgent({
1294
+ * model: "claude-sonnet-4-5-20250929",
1295
+ * subagents: [customGP],
1296
+ * // Disable the default general-purpose agent since we're providing our own
1297
+ * // (handled automatically when using createSubAgentMiddleware directly)
1298
+ * });
1299
+ * ```
1300
+ */
1301
+ const GENERAL_PURPOSE_SUBAGENT = {
1302
+ name: "general-purpose",
1303
+ description: DEFAULT_GENERAL_PURPOSE_DESCRIPTION,
1304
+ systemPrompt: DEFAULT_SUBAGENT_PROMPT
1305
+ };
1306
+ /**
1231
1307
  * Filter state to exclude certain keys when passing to subagents
1232
1308
  */
1233
1309
  function filterStateForSubagent(state) {
@@ -1255,12 +1331,13 @@ function returnCommandWithStateUpdate(result, toolCallId) {
1255
1331
  * Create subagent instances from specifications
1256
1332
  */
1257
1333
  function getSubagents(options) {
1258
- const { defaultModel, defaultTools, defaultMiddleware, defaultInterruptOn, subagents, generalPurposeAgent } = options;
1334
+ const { defaultModel, defaultTools, defaultMiddleware, generalPurposeMiddleware: gpMiddleware, defaultInterruptOn, subagents, generalPurposeAgent } = options;
1259
1335
  const defaultSubagentMiddleware = defaultMiddleware || [];
1336
+ const generalPurposeMiddlewareBase = gpMiddleware || defaultSubagentMiddleware;
1260
1337
  const agents = {};
1261
1338
  const subagentDescriptions = [];
1262
1339
  if (generalPurposeAgent) {
1263
- const generalPurposeMiddleware = [...defaultSubagentMiddleware];
1340
+ const generalPurposeMiddleware = [...generalPurposeMiddlewareBase];
1264
1341
  if (defaultInterruptOn) generalPurposeMiddleware.push((0, langchain.humanInTheLoopMiddleware)({ interruptOn: defaultInterruptOn }));
1265
1342
  agents["general-purpose"] = (0, langchain.createAgent)({
1266
1343
  model: defaultModel,
@@ -1294,11 +1371,12 @@ function getSubagents(options) {
1294
1371
  * Create the task tool for invoking subagents
1295
1372
  */
1296
1373
  function createTaskTool(options) {
1297
- const { defaultModel, defaultTools, defaultMiddleware, defaultInterruptOn, subagents, generalPurposeAgent, taskDescription } = options;
1374
+ const { defaultModel, defaultTools, defaultMiddleware, generalPurposeMiddleware, defaultInterruptOn, subagents, generalPurposeAgent, taskDescription } = options;
1298
1375
  const { agents: subagentGraphs, descriptions: subagentDescriptions } = getSubagents({
1299
1376
  defaultModel,
1300
1377
  defaultTools,
1301
1378
  defaultMiddleware,
1379
+ generalPurposeMiddleware,
1302
1380
  defaultInterruptOn,
1303
1381
  subagents,
1304
1382
  generalPurposeAgent
@@ -1328,13 +1406,14 @@ function createTaskTool(options) {
1328
1406
  * Create subagent middleware with task tool
1329
1407
  */
1330
1408
  function createSubAgentMiddleware(options) {
1331
- const { defaultModel, defaultTools = [], defaultMiddleware = null, defaultInterruptOn = null, subagents = [], systemPrompt = TASK_SYSTEM_PROMPT, generalPurposeAgent = true, taskDescription = null } = options;
1409
+ const { defaultModel, defaultTools = [], defaultMiddleware = null, generalPurposeMiddleware = null, defaultInterruptOn = null, subagents = [], systemPrompt = TASK_SYSTEM_PROMPT, generalPurposeAgent = true, taskDescription = null } = options;
1332
1410
  return (0, langchain.createMiddleware)({
1333
1411
  name: "subAgentMiddleware",
1334
1412
  tools: [createTaskTool({
1335
1413
  defaultModel,
1336
1414
  defaultTools,
1337
1415
  defaultMiddleware,
1416
+ generalPurposeMiddleware,
1338
1417
  defaultInterruptOn,
1339
1418
  subagents,
1340
1419
  generalPurposeAgent,
@@ -2398,7 +2477,7 @@ var StoreBackend = class {
2398
2477
  * Security and search upgrades:
2399
2478
  * - Secure path resolution with root containment when in virtual_mode (sandboxed to cwd)
2400
2479
  * - Prevent symlink-following on file I/O using O_NOFOLLOW when available
2401
- * - Ripgrep-powered grep with JSON parsing, plus regex fallback
2480
+ * - Ripgrep-powered grep with literal (fixed-string) search, plus substring fallback
2402
2481
  * and optional glob include filtering, while preserving virtual path behavior
2403
2482
  */
2404
2483
  const SUPPORTS_NOFOLLOW = node_fs.default.constants.O_NOFOLLOW !== void 0;
@@ -2647,14 +2726,16 @@ var FilesystemBackend = class {
2647
2726
  }
2648
2727
  }
2649
2728
  /**
2650
- * Structured search results or error string for invalid input.
2729
+ * Search for a literal text pattern in files.
2730
+ *
2731
+ * Uses ripgrep if available, falling back to substring search.
2732
+ *
2733
+ * @param pattern - Literal string to search for (NOT regex).
2734
+ * @param dirPath - Directory or file path to search in. Defaults to current directory.
2735
+ * @param glob - Optional glob pattern to filter which files to search.
2736
+ * @returns List of GrepMatch dicts containing path, line number, and matched text.
2651
2737
  */
2652
2738
  async grepRaw(pattern, dirPath = "/", glob = null) {
2653
- try {
2654
- new RegExp(pattern);
2655
- } catch (e) {
2656
- return `Invalid regex pattern: ${e.message}`;
2657
- }
2658
2739
  let baseFull;
2659
2740
  try {
2660
2741
  baseFull = this.resolvePath(dirPath || ".");
@@ -2667,7 +2748,7 @@ var FilesystemBackend = class {
2667
2748
  return [];
2668
2749
  }
2669
2750
  let results = await this.ripgrepSearch(pattern, baseFull, glob);
2670
- if (results === null) results = await this.pythonSearch(pattern, baseFull, glob);
2751
+ if (results === null) results = await this.literalSearch(pattern, baseFull, glob);
2671
2752
  const matches = [];
2672
2753
  for (const [fpath, items] of Object.entries(results)) for (const [lineNum, lineText] of items) matches.push({
2673
2754
  path: fpath,
@@ -2677,12 +2758,17 @@ var FilesystemBackend = class {
2677
2758
  return matches;
2678
2759
  }
2679
2760
  /**
2680
- * Try to use ripgrep for fast searching.
2681
- * Returns null if ripgrep is not available or fails.
2761
+ * Search using ripgrep with fixed-string (literal) mode.
2762
+ *
2763
+ * @param pattern - Literal string to search for (unescaped).
2764
+ * @param baseFull - Resolved base path to search in.
2765
+ * @param includeGlob - Optional glob pattern to filter files.
2766
+ * @returns Dict mapping file paths to list of (line_number, line_text) tuples.
2767
+ * Returns null if ripgrep is unavailable or times out.
2682
2768
  */
2683
2769
  async ripgrepSearch(pattern, baseFull, includeGlob) {
2684
2770
  return new Promise((resolve) => {
2685
- const args = ["--json"];
2771
+ const args = ["--json", "-F"];
2686
2772
  if (includeGlob) args.push("--glob", includeGlob);
2687
2773
  args.push("--", pattern, baseFull);
2688
2774
  const proc = (0, node_child_process.spawn)("rg", args, { timeout: 3e4 });
@@ -2731,15 +2817,16 @@ var FilesystemBackend = class {
2731
2817
  });
2732
2818
  }
2733
2819
  /**
2734
- * Fallback regex search implementation.
2820
+ * Fallback search using literal substring matching when ripgrep is unavailable.
2821
+ *
2822
+ * Recursively searches files, respecting maxFileSizeBytes limit.
2823
+ *
2824
+ * @param pattern - Literal string to search for.
2825
+ * @param baseFull - Resolved base path to search in.
2826
+ * @param includeGlob - Optional glob pattern to filter files by name.
2827
+ * @returns Dict mapping file paths to list of (line_number, line_text) tuples.
2735
2828
  */
2736
- async pythonSearch(pattern, baseFull, includeGlob) {
2737
- let regex;
2738
- try {
2739
- regex = new RegExp(pattern);
2740
- } catch {
2741
- return {};
2742
- }
2829
+ async literalSearch(pattern, baseFull, includeGlob) {
2743
2830
  const results = {};
2744
2831
  const files = await (0, fast_glob.default)("**/*", {
2745
2832
  cwd: (await node_fs_promises.default.stat(baseFull)).isDirectory() ? baseFull : node_path.default.dirname(baseFull),
@@ -2753,7 +2840,7 @@ var FilesystemBackend = class {
2753
2840
  const lines = (await node_fs_promises.default.readFile(fp, "utf-8")).split("\n");
2754
2841
  for (let i = 0; i < lines.length; i++) {
2755
2842
  const line = lines[i];
2756
- if (regex.test(line)) {
2843
+ if (line.includes(pattern)) {
2757
2844
  let virtPath;
2758
2845
  if (this.virtualMode) try {
2759
2846
  const relative = node_path.default.relative(this.cwd, fp);
@@ -3317,7 +3404,11 @@ console.log(count);
3317
3404
  "`;
3318
3405
  }
3319
3406
  /**
3320
- * Node.js command template for grep operations.
3407
+ * Node.js command template for grep operations with literal (fixed-string) search.
3408
+ *
3409
+ * @param pattern - Literal string to search for (NOT regex).
3410
+ * @param searchPath - Base path to search in.
3411
+ * @param globPattern - Optional glob pattern to filter files.
3321
3412
  */
3322
3413
  function buildGrepCommand(pattern, searchPath, globPattern) {
3323
3414
  const patternB64 = btoa(pattern);
@@ -3331,14 +3422,6 @@ const pattern = atob('${patternB64}');
3331
3422
  const searchPath = atob('${pathB64}');
3332
3423
  const globPattern = ${globPattern ? `atob('${globB64}')` : "null"};
3333
3424
 
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
3425
  function globMatch(filePath, pattern) {
3343
3426
  if (!pattern) return true;
3344
3427
  const regexPattern = pattern
@@ -3363,7 +3446,8 @@ function walkDir(dir, results) {
3363
3446
  const content = fs.readFileSync(fullPath, 'utf-8');
3364
3447
  const lines = content.split('\\n');
3365
3448
  for (let i = 0; i < lines.length; i++) {
3366
- if (regex.test(lines[i])) {
3449
+ // Simple substring search for literal matching
3450
+ if (lines[i].includes(pattern)) {
3367
3451
  console.log(JSON.stringify({
3368
3452
  path: fullPath,
3369
3453
  line: i + 1,
@@ -3459,14 +3543,16 @@ var BaseSandbox = class {
3459
3543
  };
3460
3544
  }
3461
3545
  /**
3462
- * Structured search results or error string for invalid input.
3546
+ * Search for a literal text pattern in files.
3547
+ *
3548
+ * @param pattern - Literal string to search for (NOT regex).
3549
+ * @param path - Directory or file path to search in.
3550
+ * @param glob - Optional glob pattern to filter which files to search.
3551
+ * @returns List of GrepMatch dicts containing path, line number, and matched text.
3463
3552
  */
3464
3553
  async grepRaw(pattern, path = "/", glob = null) {
3465
3554
  const command = buildGrepCommand(pattern, path, glob);
3466
3555
  const result = await this.execute(command);
3467
- if (result.exitCode === 1) {
3468
- if (result.output.includes("Invalid regex:")) return result.output.trim();
3469
- }
3470
3556
  const matches = [];
3471
3557
  const lines = result.output.trim().split("\n").filter(Boolean);
3472
3558
  for (const line of lines) try {
@@ -3595,6 +3681,51 @@ function createDeepAgent(params = {}) {
3595
3681
  sources: memory
3596
3682
  })] : [];
3597
3683
  /**
3684
+ * Process subagents to add SkillsMiddleware for those with their own skills.
3685
+ *
3686
+ * Custom subagents do NOT inherit skills from the main agent by default.
3687
+ * Only the general-purpose subagent inherits the main agent's skills (via defaultMiddleware).
3688
+ * If a custom subagent needs skills, it must specify its own `skills` array.
3689
+ */
3690
+ const processedSubagents = subagents.map((subagent) => {
3691
+ /**
3692
+ * CompiledSubAgent - use as-is (already has its own middleware baked in)
3693
+ */
3694
+ if (_langchain_core_runnables.Runnable.isRunnable(subagent)) return subagent;
3695
+ /**
3696
+ * SubAgent without skills - use as-is
3697
+ */
3698
+ if (!("skills" in subagent) || subagent.skills?.length === 0) return subagent;
3699
+ /**
3700
+ * SubAgent with skills - add SkillsMiddleware BEFORE user's middleware
3701
+ * Order: base middleware (via defaultMiddleware) → skills → user's middleware
3702
+ * This matches Python's ordering in create_deep_agent
3703
+ */
3704
+ const subagentSkillsMiddleware = createSkillsMiddleware({
3705
+ backend: filesystemBackend,
3706
+ sources: subagent.skills ?? []
3707
+ });
3708
+ return {
3709
+ ...subagent,
3710
+ middleware: [subagentSkillsMiddleware, ...subagent.middleware || []]
3711
+ };
3712
+ });
3713
+ /**
3714
+ * Middleware for custom subagents (does NOT include skills from main agent).
3715
+ * Custom subagents must define their own `skills` property to get skills.
3716
+ */
3717
+ const subagentMiddleware = [
3718
+ (0, langchain.todoListMiddleware)(),
3719
+ createFilesystemMiddleware({ backend: filesystemBackend }),
3720
+ (0, langchain.summarizationMiddleware)({
3721
+ model,
3722
+ trigger: { tokens: 17e4 },
3723
+ keep: { messages: 6 }
3724
+ }),
3725
+ (0, langchain.anthropicPromptCachingMiddleware)({ unsupportedModelBehavior: "ignore" }),
3726
+ createPatchToolCallsMiddleware()
3727
+ ];
3728
+ /**
3598
3729
  * Return as DeepAgent with proper DeepAgentTypeConfig
3599
3730
  * - Response: TResponse (from responseFormat parameter)
3600
3731
  * - State: undefined (state comes from middleware)
@@ -3614,20 +3745,10 @@ function createDeepAgent(params = {}) {
3614
3745
  createSubAgentMiddleware({
3615
3746
  defaultModel: model,
3616
3747
  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
- ],
3748
+ defaultMiddleware: subagentMiddleware,
3749
+ generalPurposeMiddleware: [...subagentMiddleware, ...skillsMiddlewareArray],
3629
3750
  defaultInterruptOn: interruptOn,
3630
- subagents,
3751
+ subagents: processedSubagents,
3631
3752
  generalPurposeAgent: true
3632
3753
  }),
3633
3754
  (0, langchain.summarizationMiddleware)({
@@ -4198,12 +4319,16 @@ function listSkills(options) {
4198
4319
  //#endregion
4199
4320
  exports.BaseSandbox = BaseSandbox;
4200
4321
  exports.CompositeBackend = CompositeBackend;
4322
+ exports.DEFAULT_GENERAL_PURPOSE_DESCRIPTION = DEFAULT_GENERAL_PURPOSE_DESCRIPTION;
4323
+ exports.DEFAULT_SUBAGENT_PROMPT = DEFAULT_SUBAGENT_PROMPT;
4201
4324
  exports.FilesystemBackend = FilesystemBackend;
4325
+ exports.GENERAL_PURPOSE_SUBAGENT = GENERAL_PURPOSE_SUBAGENT;
4202
4326
  exports.MAX_SKILL_DESCRIPTION_LENGTH = MAX_SKILL_DESCRIPTION_LENGTH;
4203
4327
  exports.MAX_SKILL_FILE_SIZE = MAX_SKILL_FILE_SIZE;
4204
4328
  exports.MAX_SKILL_NAME_LENGTH = MAX_SKILL_NAME_LENGTH;
4205
4329
  exports.StateBackend = StateBackend;
4206
4330
  exports.StoreBackend = StoreBackend;
4331
+ exports.TASK_SYSTEM_PROMPT = TASK_SYSTEM_PROMPT;
4207
4332
  exports.createAgentMemoryMiddleware = createAgentMemoryMiddleware;
4208
4333
  exports.createDeepAgent = createDeepAgent;
4209
4334
  exports.createFilesystemMiddleware = createFilesystemMiddleware;