deepagents 0.2.3__py3-none-any.whl → 0.2.5__py3-none-any.whl
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/backends/__init__.py +1 -1
- deepagents/backends/composite.py +32 -42
- deepagents/backends/filesystem.py +92 -86
- deepagents/backends/protocol.py +39 -13
- deepagents/backends/state.py +59 -58
- deepagents/backends/store.py +74 -67
- deepagents/backends/utils.py +7 -21
- deepagents/graph.py +1 -1
- deepagents/middleware/filesystem.py +49 -47
- deepagents/middleware/resumable_shell.py +5 -4
- deepagents/middleware/subagents.py +1 -2
- {deepagents-0.2.3.dist-info → deepagents-0.2.5.dist-info}/METADATA +7 -7
- deepagents-0.2.5.dist-info/RECORD +38 -0
- deepagents-0.2.5.dist-info/top_level.txt +2 -0
- deepagents-cli/README.md +3 -0
- deepagents-cli/deepagents_cli/README.md +196 -0
- deepagents-cli/deepagents_cli/__init__.py +5 -0
- deepagents-cli/deepagents_cli/__main__.py +6 -0
- deepagents-cli/deepagents_cli/agent.py +278 -0
- {deepagents/middleware → deepagents-cli/deepagents_cli}/agent_memory.py +16 -12
- deepagents-cli/deepagents_cli/commands.py +89 -0
- deepagents-cli/deepagents_cli/config.py +118 -0
- deepagents-cli/deepagents_cli/execution.py +636 -0
- deepagents-cli/deepagents_cli/file_ops.py +347 -0
- deepagents-cli/deepagents_cli/input.py +270 -0
- deepagents-cli/deepagents_cli/main.py +226 -0
- deepagents-cli/deepagents_cli/py.typed +0 -0
- deepagents-cli/deepagents_cli/token_utils.py +63 -0
- deepagents-cli/deepagents_cli/tools.py +140 -0
- deepagents-cli/deepagents_cli/ui.py +489 -0
- deepagents-cli/tests/test_file_ops.py +119 -0
- deepagents-cli/tests/test_placeholder.py +5 -0
- deepagents-0.2.3.dist-info/RECORD +0 -21
- deepagents-0.2.3.dist-info/top_level.txt +0 -1
- {deepagents-0.2.3.dist-info → deepagents-0.2.5.dist-info}/WHEEL +0 -0
- {deepagents-0.2.3.dist-info → deepagents-0.2.5.dist-info}/licenses/LICENSE +0 -0
- {deepagents → deepagents-cli/deepagents_cli}/default_agent_prompt.md +0 -0
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"""Middleware for providing filesystem tools to an agent."""
|
|
2
2
|
# ruff: noqa: E501
|
|
3
3
|
|
|
4
|
-
from collections.abc import Awaitable, Callable, Sequence
|
|
5
|
-
from typing import Annotated
|
|
6
|
-
from typing_extensions import NotRequired
|
|
7
|
-
|
|
8
4
|
import os
|
|
9
|
-
from
|
|
5
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
6
|
+
from typing import Annotated, Literal, NotRequired
|
|
10
7
|
|
|
11
8
|
from langchain.agents.middleware.types import (
|
|
12
9
|
AgentMiddleware,
|
|
@@ -21,14 +18,13 @@ from langchain_core.tools import BaseTool, tool
|
|
|
21
18
|
from langgraph.types import Command
|
|
22
19
|
from typing_extensions import TypedDict
|
|
23
20
|
|
|
24
|
-
from deepagents.backends.protocol import BackendProtocol, BackendFactory, WriteResult, EditResult
|
|
25
21
|
from deepagents.backends import StateBackend
|
|
22
|
+
from deepagents.backends.protocol import BackendFactory, BackendProtocol, EditResult, WriteResult
|
|
26
23
|
from deepagents.backends.utils import (
|
|
27
|
-
update_file_data,
|
|
28
24
|
format_content_with_line_numbers,
|
|
29
25
|
format_grep_matches,
|
|
30
|
-
truncate_if_too_long,
|
|
31
26
|
sanitize_tool_call_id,
|
|
27
|
+
truncate_if_too_long,
|
|
32
28
|
)
|
|
33
29
|
|
|
34
30
|
EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents"
|
|
@@ -36,10 +32,7 @@ MAX_LINE_LENGTH = 2000
|
|
|
36
32
|
LINE_NUMBER_WIDTH = 6
|
|
37
33
|
DEFAULT_READ_OFFSET = 0
|
|
38
34
|
DEFAULT_READ_LIMIT = 500
|
|
39
|
-
BACKEND_TYPES =
|
|
40
|
-
BackendProtocol
|
|
41
|
-
| BackendFactory
|
|
42
|
-
)
|
|
35
|
+
BACKEND_TYPES = BackendProtocol | BackendFactory
|
|
43
36
|
|
|
44
37
|
|
|
45
38
|
class FileData(TypedDict):
|
|
@@ -135,6 +128,7 @@ def _validate_path(path: str, *, allowed_prefixes: Sequence[str] | None = None)
|
|
|
135
128
|
|
|
136
129
|
return normalized
|
|
137
130
|
|
|
131
|
+
|
|
138
132
|
class FilesystemState(AgentState):
|
|
139
133
|
"""State for the filesystem middleware."""
|
|
140
134
|
|
|
@@ -327,15 +321,17 @@ def _write_file_tool_generator(
|
|
|
327
321
|
return res.error
|
|
328
322
|
# If backend returns state update, wrap into Command with ToolMessage
|
|
329
323
|
if res.files_update is not None:
|
|
330
|
-
return Command(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
324
|
+
return Command(
|
|
325
|
+
update={
|
|
326
|
+
"files": res.files_update,
|
|
327
|
+
"messages": [
|
|
328
|
+
ToolMessage(
|
|
329
|
+
content=f"Updated file {res.path}",
|
|
330
|
+
tool_call_id=runtime.tool_call_id,
|
|
331
|
+
)
|
|
332
|
+
],
|
|
333
|
+
}
|
|
334
|
+
)
|
|
339
335
|
return f"Updated file {res.path}"
|
|
340
336
|
|
|
341
337
|
return write_file
|
|
@@ -371,15 +367,17 @@ def _edit_file_tool_generator(
|
|
|
371
367
|
if res.error:
|
|
372
368
|
return res.error
|
|
373
369
|
if res.files_update is not None:
|
|
374
|
-
return Command(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
370
|
+
return Command(
|
|
371
|
+
update={
|
|
372
|
+
"files": res.files_update,
|
|
373
|
+
"messages": [
|
|
374
|
+
ToolMessage(
|
|
375
|
+
content=f"Successfully replaced {res.occurrences} instance(s) of the string in '{res.path}'",
|
|
376
|
+
tool_call_id=runtime.tool_call_id,
|
|
377
|
+
)
|
|
378
|
+
],
|
|
379
|
+
}
|
|
380
|
+
)
|
|
383
381
|
return f"Successfully replaced {res.occurrences} instance(s) of the string in '{res.path}'"
|
|
384
382
|
|
|
385
383
|
return edit_file
|
|
@@ -428,7 +426,7 @@ def _grep_tool_generator(
|
|
|
428
426
|
def grep(
|
|
429
427
|
pattern: str,
|
|
430
428
|
runtime: ToolRuntime[None, FilesystemState],
|
|
431
|
-
path:
|
|
429
|
+
path: str | None = None,
|
|
432
430
|
glob: str | None = None,
|
|
433
431
|
output_mode: Literal["files_with_matches", "content", "count"] = "files_with_matches",
|
|
434
432
|
) -> str:
|
|
@@ -509,10 +507,7 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
509
507
|
agent = create_agent(middleware=[FilesystemMiddleware()])
|
|
510
508
|
|
|
511
509
|
# With hybrid storage (ephemeral + persistent /memories/)
|
|
512
|
-
backend = CompositeBackend(
|
|
513
|
-
default=StateBackend(),
|
|
514
|
-
routes={"/memories/": StoreBackend()}
|
|
515
|
-
)
|
|
510
|
+
backend = CompositeBackend(default=StateBackend(), routes={"/memories/": StoreBackend()})
|
|
516
511
|
agent = create_agent(middleware=[FilesystemMiddleware(memory_backend=backend)])
|
|
517
512
|
```
|
|
518
513
|
"""
|
|
@@ -608,7 +603,7 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
608
603
|
result = resolved_backend.write(file_path, content)
|
|
609
604
|
if result.error:
|
|
610
605
|
return message, None
|
|
611
|
-
content_sample = format_content_with_line_numbers(content.splitlines()[:10], start_line=1)
|
|
606
|
+
content_sample = format_content_with_line_numbers([line[:1000] for line in content.splitlines()[:10]], start_line=1)
|
|
612
607
|
processed_message = ToolMessage(
|
|
613
608
|
TOO_LARGE_TOOL_MSG.format(
|
|
614
609
|
tool_call_id=message.tool_call_id,
|
|
@@ -621,20 +616,25 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
621
616
|
|
|
622
617
|
def _intercept_large_tool_result(self, tool_result: ToolMessage | Command, runtime: ToolRuntime) -> ToolMessage | Command:
|
|
623
618
|
if isinstance(tool_result, ToolMessage) and isinstance(tool_result.content, str):
|
|
624
|
-
if not (self.tool_token_limit_before_evict and
|
|
625
|
-
len(tool_result.content) > 4 * self.tool_token_limit_before_evict):
|
|
619
|
+
if not (self.tool_token_limit_before_evict and len(tool_result.content) > 4 * self.tool_token_limit_before_evict):
|
|
626
620
|
return tool_result
|
|
627
621
|
resolved_backend = self._get_backend(runtime)
|
|
628
622
|
processed_message, files_update = self._process_large_message(
|
|
629
623
|
tool_result,
|
|
630
624
|
resolved_backend,
|
|
631
625
|
)
|
|
632
|
-
return (
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
626
|
+
return (
|
|
627
|
+
Command(
|
|
628
|
+
update={
|
|
629
|
+
"files": files_update,
|
|
630
|
+
"messages": [processed_message],
|
|
631
|
+
}
|
|
632
|
+
)
|
|
633
|
+
if files_update is not None
|
|
634
|
+
else processed_message
|
|
635
|
+
)
|
|
636
636
|
|
|
637
|
-
|
|
637
|
+
if isinstance(tool_result, Command):
|
|
638
638
|
update = tool_result.update
|
|
639
639
|
if update is None:
|
|
640
640
|
return tool_result
|
|
@@ -643,10 +643,12 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
643
643
|
resolved_backend = self._get_backend(runtime)
|
|
644
644
|
processed_messages = []
|
|
645
645
|
for message in command_messages:
|
|
646
|
-
if not (
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
646
|
+
if not (
|
|
647
|
+
self.tool_token_limit_before_evict
|
|
648
|
+
and isinstance(message, ToolMessage)
|
|
649
|
+
and isinstance(message.content, str)
|
|
650
|
+
and len(message.content) > 4 * self.tool_token_limit_before_evict
|
|
651
|
+
):
|
|
650
652
|
processed_messages.append(message)
|
|
651
653
|
continue
|
|
652
654
|
processed_message, files_update = self._process_large_message(
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
from typing import Any, cast
|
|
6
7
|
|
|
7
8
|
from langchain.agents.middleware.shell_tool import (
|
|
8
9
|
ShellToolMiddleware,
|
|
10
|
+
ShellToolState,
|
|
9
11
|
_PersistentShellTool,
|
|
10
12
|
_SessionResources,
|
|
11
|
-
ShellToolState,
|
|
12
13
|
)
|
|
13
14
|
from langchain.agents.middleware.types import AgentState
|
|
14
|
-
from langchain_core.messages import ToolMessage
|
|
15
15
|
from langchain.tools.tool_node import ToolCallRequest
|
|
16
|
+
from langchain_core.messages import ToolMessage
|
|
16
17
|
from langgraph.types import Command
|
|
17
18
|
|
|
18
19
|
|
|
@@ -78,7 +79,7 @@ class ResumableShellToolMiddleware(ShellToolMiddleware):
|
|
|
78
79
|
return resources
|
|
79
80
|
|
|
80
81
|
new_resources = self._create_resources()
|
|
81
|
-
cast(dict[str, Any], state)["shell_session_resources"] = new_resources
|
|
82
|
+
cast("dict[str, Any]", state)["shell_session_resources"] = new_resources
|
|
82
83
|
return new_resources
|
|
83
84
|
|
|
84
85
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"""Middleware for providing subagents to an agent via a `task` tool."""
|
|
2
2
|
|
|
3
3
|
from collections.abc import Awaitable, Callable, Sequence
|
|
4
|
-
from typing import Any, TypedDict, cast
|
|
5
|
-
from typing_extensions import NotRequired
|
|
4
|
+
from typing import Any, NotRequired, TypedDict, cast
|
|
6
5
|
|
|
7
6
|
from langchain.agents import create_agent
|
|
8
7
|
from langchain.agents.middleware import HumanInTheLoopMiddleware, InterruptOnConfig
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
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
|
+
Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
|
|
7
|
+
Project-URL: Documentation, https://reference.langchain.com/python/deepagents/
|
|
8
|
+
Project-URL: Source, https://github.com/langchain-ai/deepagents
|
|
9
|
+
Project-URL: Twitter, https://x.com/LangChainAI
|
|
10
|
+
Project-URL: Slack, https://www.langchain.com/join-community
|
|
11
|
+
Project-URL: Reddit, https://www.reddit.com/r/LangChain/
|
|
6
12
|
Requires-Python: <4.0,>=3.11
|
|
7
13
|
Description-Content-Type: text/markdown
|
|
8
14
|
License-File: LICENSE
|
|
@@ -10,12 +16,6 @@ Requires-Dist: langchain-anthropic<2.0.0,>=1.0.0
|
|
|
10
16
|
Requires-Dist: langchain<2.0.0,>=1.0.2
|
|
11
17
|
Requires-Dist: langchain-core<2.0.0,>=1.0.0
|
|
12
18
|
Requires-Dist: wcmatch
|
|
13
|
-
Provides-Extra: dev
|
|
14
|
-
Requires-Dist: pytest; extra == "dev"
|
|
15
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
16
|
-
Requires-Dist: build; extra == "dev"
|
|
17
|
-
Requires-Dist: twine; extra == "dev"
|
|
18
|
-
Requires-Dist: langchain-openai; extra == "dev"
|
|
19
19
|
Dynamic: license-file
|
|
20
20
|
|
|
21
21
|
# 🧠🤖Deep Agents
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
deepagents/__init__.py,sha256=9BVNn4lfF5N8l2KY8Ttxi82zO609I-fGqoSIF7DAxiU,342
|
|
2
|
+
deepagents/graph.py,sha256=pwRKF9EsmBtc2LabAcqted92kIhG5b4SaEfACT2G4Ps,6094
|
|
3
|
+
deepagents/backends/__init__.py,sha256=BOKu2cQ1OdMyO_l2rLqZQiXppYFmQbx7OIQb7WYwvZc,457
|
|
4
|
+
deepagents/backends/composite.py,sha256=0jvuo2YBlr3LT3N10uE0p1-vVSCdkzK0uLciPRcUg8o,8431
|
|
5
|
+
deepagents/backends/filesystem.py,sha256=SsVDx__j_AARIwRzaDuokbxbkquJ_Lw3Qi7dLWaqRUs,18674
|
|
6
|
+
deepagents/backends/protocol.py,sha256=wmXk24BBN1vo3ALYTIZmCi5fjxNZeH8y06v1OXc5ABU,4555
|
|
7
|
+
deepagents/backends/state.py,sha256=ST_tUExPxArJaA3U8vc1dyzxuYl2BQH-HU7P0eu_Ty8,6518
|
|
8
|
+
deepagents/backends/store.py,sha256=HWGZ0euXp9ww4HvWYg1S-tEH17cuFy3F1lg3DiGN6CY,13232
|
|
9
|
+
deepagents/backends/utils.py,sha256=Iyk2jW-gfoLvMnz-W_2FRCoJW_j3r1zoumU9iww-jd0,13973
|
|
10
|
+
deepagents/middleware/__init__.py,sha256=x7UHqGcrKlhzORNdChPvnUwa_PIJCbFUHY6zTKVfloI,418
|
|
11
|
+
deepagents/middleware/filesystem.py,sha256=7O2GUeGPQBslNBU1Gdser-tkL2DO_f8nZ9yZsGC3INs,28294
|
|
12
|
+
deepagents/middleware/patch_tool_calls.py,sha256=PdNhxPaQqwnFkhEAZEE2kEzadTNAOO3_iJRA30WqpGE,1981
|
|
13
|
+
deepagents/middleware/resumable_shell.py,sha256=KjhafjKu28Nf_8pDmSk_aWRK7pgkXZoubvWQljIEv3w,3382
|
|
14
|
+
deepagents/middleware/subagents.py,sha256=dI9w_TXKb4I6-InHua993sICTQ4fw5GapgNkHc-q50I,23474
|
|
15
|
+
deepagents-0.2.5.dist-info/licenses/LICENSE,sha256=c__BaxUCK69leo2yEKynf8lWndu8iwYwge1CbyqAe-E,1071
|
|
16
|
+
deepagents-cli/README.md,sha256=LQksI_Z2qG3IMbIZ7ZRBQlWTufPZmIevGt7uya_HObg,49
|
|
17
|
+
deepagents-cli/deepagents_cli/README.md,sha256=ajP946mBaKQRn_k3vViWfQn8v1BKiaw58gqb0xKmp4o,7413
|
|
18
|
+
deepagents-cli/deepagents_cli/__init__.py,sha256=2W9tHzQianR_Q0ku9hc_ZI3kUYsilXQ6I_kUnpg9bzg,108
|
|
19
|
+
deepagents-cli/deepagents_cli/__main__.py,sha256=J9-RNZv_zxw4SyjWRI_N7k7m8G4--vclD8vKxYIiXPQ,128
|
|
20
|
+
deepagents-cli/deepagents_cli/agent.py,sha256=8GFi9XtfedYTL0J-f72ARb_glklIIFCyzI_9buAg3h4,11066
|
|
21
|
+
deepagents-cli/deepagents_cli/agent_memory.py,sha256=Ma4P8sFpZ84ytb8GtqRcTv9Uy5E15oXqJzuBV9vuKmU,9147
|
|
22
|
+
deepagents-cli/deepagents_cli/commands.py,sha256=rHsHij1xGs13N_m46YcBP2M2_MRGqRbSlnWaZaMnoco,2606
|
|
23
|
+
deepagents-cli/deepagents_cli/config.py,sha256=gcYPDPltyhgMIiTNXQOaoKGqsAc-izNGPOlAqf_o7I0,4403
|
|
24
|
+
deepagents-cli/deepagents_cli/default_agent_prompt.md,sha256=Pi9SvgOAa74qhgDUMexm2-S_OfUO_CTic3zdbUnuB4s,4964
|
|
25
|
+
deepagents-cli/deepagents_cli/execution.py,sha256=2VqjnrT_LQn0b7eLqIIPW6oB4GeLFSR1w2X_3kjU4Lg,26498
|
|
26
|
+
deepagents-cli/deepagents_cli/file_ops.py,sha256=LQ7NTXPWLwePbiTBDA-22_VHxEGil7NpBltHZx1C7r4,12362
|
|
27
|
+
deepagents-cli/deepagents_cli/input.py,sha256=KXv9Kq13V1fK5bSRLfJxheYn8FH6_xc_l4fpcQlwqNc,10177
|
|
28
|
+
deepagents-cli/deepagents_cli/main.py,sha256=cIyLfAU-eJrAf2i7aCEElVTO7xXPXW8jsje4REACfcg,7142
|
|
29
|
+
deepagents-cli/deepagents_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
deepagents-cli/deepagents_cli/token_utils.py,sha256=tEeghLW2-vsitc-ba9cklKYnjLgRpvK7rK7PRiUu8jA,2439
|
|
31
|
+
deepagents-cli/deepagents_cli/tools.py,sha256=Av92Luq-vGgUr25DqErGi7aI6y6DdFSXLigffhNLxYk,4287
|
|
32
|
+
deepagents-cli/deepagents_cli/ui.py,sha256=d5hMHuTYerAINehGHohTI0ln4u9vojPvK1fN4OrqEEI,18667
|
|
33
|
+
deepagents-cli/tests/test_file_ops.py,sha256=xtu1UCmVf6gPadK0H33CtscCon8GAChi5oCboZfq-hg,3008
|
|
34
|
+
deepagents-cli/tests/test_placeholder.py,sha256=awIMcOR8rWGq0p_kljsIkZKinVdDlyx3MqDMQgPSi6U,101
|
|
35
|
+
deepagents-0.2.5.dist-info/METADATA,sha256=PiLwTOCG0BRgMteCvhAf4JhOOYxZW3EPq5Qb0_95fec,18828
|
|
36
|
+
deepagents-0.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
37
|
+
deepagents-0.2.5.dist-info/top_level.txt,sha256=7lDw_vmqmbQPyEVg2wcsIMEM9rjFC8i4pDXlksJ7vao,26
|
|
38
|
+
deepagents-0.2.5.dist-info/RECORD,,
|
deepagents-cli/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# DeepAgents CLI
|
|
2
|
+
|
|
3
|
+
Interactive command-line interface for DeepAgents - an AI coding assistant with file operations, web search, and shell command execution.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
The CLI is organized into focused modules:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
cli/
|
|
11
|
+
├── __init__.py # Package exports
|
|
12
|
+
├── __main__.py # Entry point for `python -m deepagents.cli`
|
|
13
|
+
├── main.py # CLI loop, argument parsing, main orchestration
|
|
14
|
+
├── config.py # Configuration, constants, colors, model creation
|
|
15
|
+
├── tools.py # Custom tools (http_request, web_search)
|
|
16
|
+
├── ui.py # Display logic, TokenTracker, help screens
|
|
17
|
+
├── input.py # Input handling, completers, prompt session
|
|
18
|
+
├── commands.py # Slash command and bash command handlers
|
|
19
|
+
├── execution.py # Task execution, streaming, HITL approval
|
|
20
|
+
└── agent.py # Agent creation, management, listing, reset
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Module Responsibilities
|
|
24
|
+
|
|
25
|
+
### `main.py` - Entry Point & Main Loop
|
|
26
|
+
- **Purpose**: CLI entry point, argument parsing, main interactive loop
|
|
27
|
+
- **Key Functions**:
|
|
28
|
+
- `cli_main()` - Console script entry point (called when you run `deepagents`)
|
|
29
|
+
- `main()` - Async main function that orchestrates agent creation and CLI
|
|
30
|
+
- `simple_cli()` - Main interactive loop handling user input
|
|
31
|
+
- `parse_args()` - Command-line argument parsing
|
|
32
|
+
- `check_cli_dependencies()` - Validates required packages are installed
|
|
33
|
+
|
|
34
|
+
### `config.py` - Configuration & Constants
|
|
35
|
+
- **Purpose**: Centralized configuration, constants, and model creation
|
|
36
|
+
- **Key Exports**:
|
|
37
|
+
- `COLORS` - Color scheme for terminal output
|
|
38
|
+
- `DEEP_AGENTS_ASCII` - ASCII art banner
|
|
39
|
+
- `COMMANDS` - Available slash commands
|
|
40
|
+
- `console` - Rich Console instance
|
|
41
|
+
- `create_model()` - Creates OpenAI or Anthropic model based on API keys
|
|
42
|
+
- `get_default_coding_instructions()` - Loads default agent prompt
|
|
43
|
+
|
|
44
|
+
### `tools.py` - Custom Agent Tools
|
|
45
|
+
- **Purpose**: Additional tools for the agent beyond built-in filesystem operations
|
|
46
|
+
- **Tools**:
|
|
47
|
+
- `http_request()` - Make HTTP requests to APIs
|
|
48
|
+
- `web_search()` - Search the web using Tavily API
|
|
49
|
+
- `tavily_client` - Initialized Tavily client (if API key available)
|
|
50
|
+
|
|
51
|
+
### `ui.py` - Display & Rendering
|
|
52
|
+
- **Purpose**: All UI rendering and display logic
|
|
53
|
+
- **Key Components**:
|
|
54
|
+
- `TokenTracker` - Track and display token usage across the session
|
|
55
|
+
- `render_todo_list()` - Render todo list with checkboxes
|
|
56
|
+
- `show_interactive_help()` - Display available commands during session
|
|
57
|
+
- `show_help()` - Full help screen
|
|
58
|
+
- `format_tool_message_content()` - Format tool messages for display
|
|
59
|
+
- `truncate_value()` - Truncate long values for readable display
|
|
60
|
+
|
|
61
|
+
### `input.py` - Input Handling
|
|
62
|
+
- **Purpose**: User input, completers, and prompt session configuration
|
|
63
|
+
- **Key Components**:
|
|
64
|
+
- `FilePathCompleter` - Autocomplete for `@file` mentions
|
|
65
|
+
- `CommandCompleter` - Autocomplete for `/commands`
|
|
66
|
+
- `BashCompleter` - Autocomplete for `!bash` commands
|
|
67
|
+
- `parse_file_mentions()` - Extract `@file` mentions and inject content
|
|
68
|
+
- `create_prompt_session()` - Configure prompt_toolkit session with:
|
|
69
|
+
- Multi-line input (Alt+Enter for newlines, Enter to submit)
|
|
70
|
+
- Command history
|
|
71
|
+
- File/command autocomplete
|
|
72
|
+
- External editor support (Ctrl+E)
|
|
73
|
+
|
|
74
|
+
### `commands.py` - Command Handlers
|
|
75
|
+
- **Purpose**: Handle slash commands (`/help`, `/clear`, etc.) and bash execution
|
|
76
|
+
- **Key Functions**:
|
|
77
|
+
- `handle_command()` - Route and execute slash commands
|
|
78
|
+
- `execute_bash_command()` - Execute bash commands prefixed with `!`
|
|
79
|
+
|
|
80
|
+
### `execution.py` - Task Execution & Streaming
|
|
81
|
+
- **Purpose**: Core execution logic, streaming responses, HITL (Human-in-the-Loop)
|
|
82
|
+
- **Key Functions**:
|
|
83
|
+
- `execute_task()` - Main execution function that:
|
|
84
|
+
- Parses file mentions
|
|
85
|
+
- Streams agent responses
|
|
86
|
+
- Displays tool calls with icons
|
|
87
|
+
- Renders todo list updates
|
|
88
|
+
- Tracks token usage
|
|
89
|
+
- Handles Ctrl+C interruptions
|
|
90
|
+
- `prompt_for_shell_approval()` - Interactive shell command approval with arrow keys
|
|
91
|
+
- **Features**:
|
|
92
|
+
- Dual-stream mode (messages + updates) for HITL support
|
|
93
|
+
- Real-time todo list rendering
|
|
94
|
+
- Spinner with status updates
|
|
95
|
+
- Token tracking integration
|
|
96
|
+
|
|
97
|
+
### `agent.py` - Agent Management
|
|
98
|
+
- **Purpose**: Agent creation, configuration, and management commands
|
|
99
|
+
- **Key Functions**:
|
|
100
|
+
- `create_agent_with_config()` - Create agent with:
|
|
101
|
+
- Filesystem backends (working directory + agent directory)
|
|
102
|
+
- Long-term memory middleware
|
|
103
|
+
- Shell execution with HITL approval
|
|
104
|
+
- Custom system prompt
|
|
105
|
+
- `list_agents()` - List all agents in `~/.deepagents/`
|
|
106
|
+
- `reset_agent()` - Reset agent to default or copy from another agent
|
|
107
|
+
|
|
108
|
+
## Data Flow
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
User Input
|
|
112
|
+
↓
|
|
113
|
+
main.py (simple_cli)
|
|
114
|
+
↓
|
|
115
|
+
input.py (parse_file_mentions, completers)
|
|
116
|
+
↓
|
|
117
|
+
execution.py (execute_task)
|
|
118
|
+
↓
|
|
119
|
+
agent.py (agent created with tools from tools.py)
|
|
120
|
+
↓
|
|
121
|
+
Stream responses → ui.py (render todos, display tool calls)
|
|
122
|
+
→ execution.py (HITL approval if needed)
|
|
123
|
+
↓
|
|
124
|
+
Display output via ui.py (TokenTracker, console)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Key Features
|
|
128
|
+
|
|
129
|
+
### File Context Injection
|
|
130
|
+
Type `@filename` and press Tab to autocomplete and inject file content into your prompt.
|
|
131
|
+
|
|
132
|
+
### Interactive Commands
|
|
133
|
+
- `/help` - Show help
|
|
134
|
+
- `/clear` - Clear screen and reset conversation
|
|
135
|
+
- `/tokens` - Show token usage
|
|
136
|
+
- `/quit` or `/exit` - Exit the CLI
|
|
137
|
+
|
|
138
|
+
### Bash Commands
|
|
139
|
+
Type `!command` to execute bash commands directly (e.g., `!ls`, `!git status`)
|
|
140
|
+
|
|
141
|
+
### Todo List Tracking
|
|
142
|
+
The agent can create and update a visual todo list for multi-step tasks.
|
|
143
|
+
|
|
144
|
+
### File Operation Summaries & Diff Viewer
|
|
145
|
+
- File reads now show a concise summary with the number of lines streamed (e.g., `⏺ Read(example.py)` followed by `⎿ Read 44 lines (lines 1-44)`).
|
|
146
|
+
- Writes and edits capture before/after snapshots, reporting lines added or removed plus bytes written.
|
|
147
|
+
- A Rich-powered unified diff renders in-line with syntax highlighting so you can review every proposed change before confirming.
|
|
148
|
+
- Diff output truncates gracefully for very large edits while still surfacing a summary.
|
|
149
|
+
- When Human-in-the-Loop approval is required, the proposed diff is shown *before* you choose Approve/Reject.
|
|
150
|
+
|
|
151
|
+
### Human-in-the-Loop Shell Approval
|
|
152
|
+
Shell commands require user approval with an interactive arrow-key menu.
|
|
153
|
+
|
|
154
|
+
### Multi-line Input
|
|
155
|
+
- **Enter** - Submit (or accept completion if menu is open)
|
|
156
|
+
- **Alt+Enter** - Insert newline (Option+Enter on Mac, or ESC then Enter)
|
|
157
|
+
- **Ctrl+E** - Open in external editor (nano by default)
|
|
158
|
+
|
|
159
|
+
## Agent Storage
|
|
160
|
+
|
|
161
|
+
Each agent stores its state in `~/.deepagents/AGENT_NAME/`:
|
|
162
|
+
- `agent.md` - Agent's custom instructions (long-term memory)
|
|
163
|
+
- `memories/` - Additional context files
|
|
164
|
+
- `history` - Command history
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
To modify the CLI:
|
|
169
|
+
|
|
170
|
+
1. **UI changes** → Edit `ui.py` or `input.py`
|
|
171
|
+
2. **Add new tools** → Edit `tools.py`
|
|
172
|
+
3. **Change execution flow** → Edit `execution.py`
|
|
173
|
+
4. **Add commands** → Edit `commands.py`
|
|
174
|
+
5. **Agent configuration** → Edit `agent.py`
|
|
175
|
+
6. **Constants/colors** → Edit `config.py`
|
|
176
|
+
|
|
177
|
+
## Running During Development
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# From project root
|
|
181
|
+
uv run python -m deepagents.cli
|
|
182
|
+
|
|
183
|
+
# Or install in editable mode
|
|
184
|
+
uv pip install -e .
|
|
185
|
+
deepagents
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Entry Point
|
|
189
|
+
|
|
190
|
+
The CLI is registered in `pyproject.toml` as:
|
|
191
|
+
```toml
|
|
192
|
+
[project.scripts]
|
|
193
|
+
deepagents = "deepagents.cli:cli_main"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
This means when users install the package, they can run `deepagents` directly.
|