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 +58 -12
- package/dist/index.cjs +201 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +402 -55
- package/dist/index.d.ts +403 -56
- package/dist/index.js +190 -70
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,25 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-

|
|
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
|
-
|
|
29
|
+
> 💡 **Tip:** Looking for the Python version of this package? See [langchain-ai/deepagents](https://github.com/langchain-ai/deepagents)
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
[](https://opensource.org/licenses/MIT)
|
|
19
|
-
[](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
|
-
|
|
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("
|
|
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
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
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 (
|
|
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
|
|
1007
|
-
if (supportsExecution)
|
|
1008
|
-
const
|
|
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
|
-
|
|
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 = [...
|
|
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
|
|
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
|
-
*
|
|
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.
|
|
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
|
-
*
|
|
2681
|
-
*
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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;
|