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 +58 -12
- package/dist/index.cjs +251 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +344 -55
- package/dist/index.d.ts +345 -56
- package/dist/index.js +239 -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="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
|
-

|
|
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");
|
|
@@ -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
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
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 (
|
|
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
|
|
1007
|
-
if (supportsExecution)
|
|
1008
|
-
const
|
|
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
|
-
|
|
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 = [...
|
|
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
|
|
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
|
-
*
|
|
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.
|
|
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
|
-
*
|
|
2681
|
-
*
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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;
|