deepagents 0.3.8__tar.gz → 0.3.9__tar.gz
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.
- {deepagents-0.3.8 → deepagents-0.3.9}/PKG-INFO +7 -7
- {deepagents-0.3.8 → deepagents-0.3.9}/README.md +3 -3
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/backends/sandbox.py +76 -23
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/middleware/filesystem.py +455 -521
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/middleware/summarization.py +9 -4
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents.egg-info/PKG-INFO +7 -7
- deepagents-0.3.9/deepagents.egg-info/requires.txt +5 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/pyproject.toml +12 -20
- deepagents-0.3.8/deepagents.egg-info/requires.txt +0 -5
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/__init__.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/backends/__init__.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/backends/composite.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/backends/filesystem.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/backends/protocol.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/backends/state.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/backends/store.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/backends/utils.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/graph.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/middleware/__init__.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/middleware/_utils.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/middleware/memory.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/middleware/patch_tool_calls.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/middleware/skills.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents/middleware/subagents.py +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents.egg-info/SOURCES.txt +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents.egg-info/dependency_links.txt +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/deepagents.egg-info/top_level.txt +0 -0
- {deepagents-0.3.8 → deepagents-0.3.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.9
|
|
4
4
|
Summary: General purpose 'deep agent' with sub-agent spawning, todo list capabilities, and mock file system. Built on LangGraph.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
|
|
@@ -11,18 +11,18 @@ Project-URL: Slack, https://www.langchain.com/join-community
|
|
|
11
11
|
Project-URL: Reddit, https://www.reddit.com/r/LangChain/
|
|
12
12
|
Requires-Python: <4.0,>=3.11
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
|
-
Requires-Dist: langchain-core<2.0.0,>=1.2.
|
|
15
|
-
Requires-Dist: langchain<2.0.0,>=1.2.
|
|
14
|
+
Requires-Dist: langchain-core<2.0.0,>=1.2.7
|
|
15
|
+
Requires-Dist: langchain<2.0.0,>=1.2.7
|
|
16
16
|
Requires-Dist: langchain-anthropic<2.0.0,>=1.3.1
|
|
17
|
-
Requires-Dist: langchain-google-genai<5.0.0,>=4.
|
|
17
|
+
Requires-Dist: langchain-google-genai<5.0.0,>=4.2.0
|
|
18
18
|
Requires-Dist: wcmatch
|
|
19
19
|
|
|
20
20
|
# 🧠🤖Deep Agents
|
|
21
21
|
|
|
22
22
|
Using an LLM to call tools in a loop is the simplest form of an agent.
|
|
23
|
-
This architecture, however, can yield agents that are
|
|
23
|
+
This architecture, however, can yield agents that are "shallow" and fail to plan and act over longer, more complex tasks.
|
|
24
24
|
|
|
25
|
-
Applications like
|
|
25
|
+
Applications like "Deep Research", "Manus", and "Claude Code" have gotten around this limitation by implementing a combination of four things:
|
|
26
26
|
a **planning tool**, **sub agents**, access to a **file system**, and a **detailed prompt**.
|
|
27
27
|
|
|
28
28
|
<img src="../../deep_agents.png" alt="deep agent" width="600"/>
|
|
@@ -249,7 +249,7 @@ class CompiledSubAgent(TypedDict):
|
|
|
249
249
|
**CompiledSubAgent fields:**
|
|
250
250
|
|
|
251
251
|
- **name**: This is the name of the subagent, and how the main agent will call the subagent
|
|
252
|
-
- **description**: This is the description of the subagent that is shown to the main agent
|
|
252
|
+
- **description**: This is the description of the subagent that is shown to the main agent
|
|
253
253
|
- **runnable**: A pre-built LangGraph graph/agent that will be used as the subagent. **Important:** The runnable's state schema must include a `messages` key. This is required for the subagent to communicate results back to the main agent.
|
|
254
254
|
|
|
255
255
|
#### Using `SubAgent`
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# 🧠🤖Deep Agents
|
|
2
2
|
|
|
3
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
|
|
4
|
+
This architecture, however, can yield agents that are "shallow" and fail to plan and act over longer, more complex tasks.
|
|
5
5
|
|
|
6
|
-
Applications like
|
|
6
|
+
Applications like "Deep Research", "Manus", and "Claude Code" have gotten around this limitation by implementing a combination of four things:
|
|
7
7
|
a **planning tool**, **sub agents**, access to a **file system**, and a **detailed prompt**.
|
|
8
8
|
|
|
9
9
|
<img src="../../deep_agents.png" alt="deep agent" width="600"/>
|
|
@@ -230,7 +230,7 @@ class CompiledSubAgent(TypedDict):
|
|
|
230
230
|
**CompiledSubAgent fields:**
|
|
231
231
|
|
|
232
232
|
- **name**: This is the name of the subagent, and how the main agent will call the subagent
|
|
233
|
-
- **description**: This is the description of the subagent that is shown to the main agent
|
|
233
|
+
- **description**: This is the description of the subagent that is shown to the main agent
|
|
234
234
|
- **runnable**: A pre-built LangGraph graph/agent that will be used as the subagent. **Important:** The runnable's state schema must include a `messages` key. This is required for the subagent to communicate results back to the main agent.
|
|
235
235
|
|
|
236
236
|
#### Using `SubAgent`
|
|
@@ -46,12 +46,32 @@ for m in matches:
|
|
|
46
46
|
print(json.dumps(result))
|
|
47
47
|
" 2>/dev/null"""
|
|
48
48
|
|
|
49
|
+
# Use heredoc to pass content via stdin to avoid ARG_MAX limits on large files.
|
|
50
|
+
# ARG_MAX limits the total size of command-line arguments.
|
|
51
|
+
# Previously, base64-encoded content was interpolated directly into the command
|
|
52
|
+
# string, which would fail for files larger than ~100KB after base64 expansion.
|
|
53
|
+
# Heredocs bypass this by passing data through stdin rather than as arguments.
|
|
54
|
+
# Stdin format: first line is base64-encoded file path, second line is base64-encoded content.
|
|
49
55
|
_WRITE_COMMAND_TEMPLATE = """python3 -c "
|
|
50
56
|
import os
|
|
51
57
|
import sys
|
|
52
58
|
import base64
|
|
59
|
+
import json
|
|
53
60
|
|
|
54
|
-
file_path
|
|
61
|
+
# Read JSON payload from stdin containing file_path and content (both base64-encoded)
|
|
62
|
+
payload_b64 = sys.stdin.read().strip()
|
|
63
|
+
if not payload_b64:
|
|
64
|
+
print('Error: No payload received for write operation', file=sys.stderr)
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
payload = base64.b64decode(payload_b64).decode('utf-8')
|
|
69
|
+
data = json.loads(payload)
|
|
70
|
+
file_path = data['path']
|
|
71
|
+
content = base64.b64decode(data['content']).decode('utf-8')
|
|
72
|
+
except Exception as e:
|
|
73
|
+
print(f'Error: Failed to decode write payload: {e}', file=sys.stderr)
|
|
74
|
+
sys.exit(1)
|
|
55
75
|
|
|
56
76
|
# Check if file already exists (atomic with write)
|
|
57
77
|
if os.path.exists(file_path):
|
|
@@ -62,24 +82,46 @@ if os.path.exists(file_path):
|
|
|
62
82
|
parent_dir = os.path.dirname(file_path) or '.'
|
|
63
83
|
os.makedirs(parent_dir, exist_ok=True)
|
|
64
84
|
|
|
65
|
-
# Decode and write content
|
|
66
|
-
content = base64.b64decode('{content_b64}').decode('utf-8')
|
|
67
85
|
with open(file_path, 'w') as f:
|
|
68
86
|
f.write(content)
|
|
69
|
-
"
|
|
70
|
-
|
|
87
|
+
" <<'__DEEPAGENTS_EOF__'
|
|
88
|
+
{payload_b64}
|
|
89
|
+
__DEEPAGENTS_EOF__"""
|
|
90
|
+
|
|
91
|
+
# Use heredoc to pass edit parameters via stdin to avoid ARG_MAX limits.
|
|
92
|
+
# Stdin format: base64-encoded JSON with {"path": str, "old": str, "new": str}.
|
|
93
|
+
# JSON bundles all parameters; base64 ensures safe transport of arbitrary content
|
|
94
|
+
# (special chars, newlines, etc.) through the heredoc without escaping issues.
|
|
71
95
|
_EDIT_COMMAND_TEMPLATE = """python3 -c "
|
|
72
96
|
import sys
|
|
73
97
|
import base64
|
|
98
|
+
import json
|
|
99
|
+
import os
|
|
100
|
+
|
|
101
|
+
# Read and decode JSON payload from stdin
|
|
102
|
+
payload_b64 = sys.stdin.read().strip()
|
|
103
|
+
if not payload_b64:
|
|
104
|
+
print('Error: No payload received for edit operation', file=sys.stderr)
|
|
105
|
+
sys.exit(4)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
payload = base64.b64decode(payload_b64).decode('utf-8')
|
|
109
|
+
data = json.loads(payload)
|
|
110
|
+
file_path = data['path']
|
|
111
|
+
old = data['old']
|
|
112
|
+
new = data['new']
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f'Error: Failed to decode edit payload: {e}', file=sys.stderr)
|
|
115
|
+
sys.exit(4)
|
|
116
|
+
|
|
117
|
+
# Check if file exists
|
|
118
|
+
if not os.path.isfile(file_path):
|
|
119
|
+
sys.exit(3) # File not found
|
|
74
120
|
|
|
75
121
|
# Read file content
|
|
76
|
-
with open(
|
|
122
|
+
with open(file_path, 'r') as f:
|
|
77
123
|
text = f.read()
|
|
78
124
|
|
|
79
|
-
# Decode base64-encoded strings
|
|
80
|
-
old = base64.b64decode('{old_b64}').decode('utf-8')
|
|
81
|
-
new = base64.b64decode('{new_b64}').decode('utf-8')
|
|
82
|
-
|
|
83
125
|
# Count occurrences
|
|
84
126
|
count = text.count(old)
|
|
85
127
|
|
|
@@ -96,11 +138,13 @@ else:
|
|
|
96
138
|
result = text.replace(old, new, 1)
|
|
97
139
|
|
|
98
140
|
# Write back to file
|
|
99
|
-
with open(
|
|
141
|
+
with open(file_path, 'w') as f:
|
|
100
142
|
f.write(result)
|
|
101
143
|
|
|
102
144
|
print(count)
|
|
103
|
-
"
|
|
145
|
+
" <<'__DEEPAGENTS_EOF__'
|
|
146
|
+
{payload_b64}
|
|
147
|
+
__DEEPAGENTS_EOF__"""
|
|
104
148
|
|
|
105
149
|
_READ_COMMAND_TEMPLATE = """python3 -c "
|
|
106
150
|
import os
|
|
@@ -221,11 +265,14 @@ except PermissionError:
|
|
|
221
265
|
content: str,
|
|
222
266
|
) -> WriteResult:
|
|
223
267
|
"""Create a new file. Returns WriteResult; error populated on failure."""
|
|
224
|
-
#
|
|
268
|
+
# Create JSON payload with file path and base64-encoded content
|
|
269
|
+
# This avoids shell injection via file_path and ARG_MAX limits on content
|
|
225
270
|
content_b64 = base64.b64encode(content.encode("utf-8")).decode("ascii")
|
|
271
|
+
payload = json.dumps({"path": file_path, "content": content_b64})
|
|
272
|
+
payload_b64 = base64.b64encode(payload.encode("utf-8")).decode("ascii")
|
|
226
273
|
|
|
227
274
|
# Single atomic check + write command
|
|
228
|
-
cmd = _WRITE_COMMAND_TEMPLATE.format(
|
|
275
|
+
cmd = _WRITE_COMMAND_TEMPLATE.format(payload_b64=payload_b64)
|
|
229
276
|
result = self.execute(cmd)
|
|
230
277
|
|
|
231
278
|
# Check for errors (exit code or error message in output)
|
|
@@ -244,23 +291,29 @@ except PermissionError:
|
|
|
244
291
|
replace_all: bool = False,
|
|
245
292
|
) -> EditResult:
|
|
246
293
|
"""Edit a file by replacing string occurrences. Returns EditResult."""
|
|
247
|
-
#
|
|
248
|
-
|
|
249
|
-
|
|
294
|
+
# Create JSON payload with file path, old string, and new string
|
|
295
|
+
# This avoids shell injection via file_path and ARG_MAX limits on strings
|
|
296
|
+
payload = json.dumps({"path": file_path, "old": old_string, "new": new_string})
|
|
297
|
+
payload_b64 = base64.b64encode(payload.encode("utf-8")).decode("ascii")
|
|
250
298
|
|
|
251
299
|
# Use template for string replacement
|
|
252
|
-
cmd = _EDIT_COMMAND_TEMPLATE.format(
|
|
300
|
+
cmd = _EDIT_COMMAND_TEMPLATE.format(payload_b64=payload_b64, replace_all=replace_all)
|
|
253
301
|
result = self.execute(cmd)
|
|
254
302
|
|
|
255
303
|
exit_code = result.exit_code
|
|
256
304
|
output = result.output.strip()
|
|
257
305
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
306
|
+
# Map exit codes to error messages
|
|
307
|
+
error_messages = {
|
|
308
|
+
1: f"Error: String not found in file: '{old_string}'",
|
|
309
|
+
2: f"Error: String '{old_string}' appears multiple times. Use replace_all=True to replace all occurrences.",
|
|
310
|
+
3: f"Error: File '{file_path}' not found",
|
|
311
|
+
4: f"Error: Failed to decode edit payload: {output}",
|
|
312
|
+
}
|
|
313
|
+
if exit_code in error_messages:
|
|
314
|
+
return EditResult(error=error_messages[exit_code])
|
|
262
315
|
if exit_code != 0:
|
|
263
|
-
return EditResult(error=f"Error
|
|
316
|
+
return EditResult(error=f"Error editing file (exit code {exit_code}): {output or 'Unknown error'}")
|
|
264
317
|
|
|
265
318
|
count = int(output)
|
|
266
319
|
# External storage - no files_update needed
|