deepagents 0.2.5__py3-none-any.whl → 0.2.7__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.
Files changed (35) hide show
  1. deepagents/backends/composite.py +37 -2
  2. deepagents/backends/protocol.py +48 -0
  3. deepagents/backends/sandbox.py +341 -0
  4. deepagents/backends/store.py +3 -11
  5. deepagents/graph.py +7 -3
  6. deepagents/middleware/__init__.py +0 -2
  7. deepagents/middleware/filesystem.py +224 -21
  8. deepagents/middleware/subagents.py +7 -4
  9. {deepagents-0.2.5.dist-info → deepagents-0.2.7.dist-info}/METADATA +5 -4
  10. deepagents-0.2.7.dist-info/RECORD +18 -0
  11. deepagents-0.2.7.dist-info/top_level.txt +1 -0
  12. deepagents/middleware/resumable_shell.py +0 -86
  13. deepagents-0.2.5.dist-info/RECORD +0 -38
  14. deepagents-0.2.5.dist-info/licenses/LICENSE +0 -21
  15. deepagents-0.2.5.dist-info/top_level.txt +0 -2
  16. deepagents-cli/README.md +0 -3
  17. deepagents-cli/deepagents_cli/README.md +0 -196
  18. deepagents-cli/deepagents_cli/__init__.py +0 -5
  19. deepagents-cli/deepagents_cli/__main__.py +0 -6
  20. deepagents-cli/deepagents_cli/agent.py +0 -278
  21. deepagents-cli/deepagents_cli/agent_memory.py +0 -226
  22. deepagents-cli/deepagents_cli/commands.py +0 -89
  23. deepagents-cli/deepagents_cli/config.py +0 -118
  24. deepagents-cli/deepagents_cli/default_agent_prompt.md +0 -110
  25. deepagents-cli/deepagents_cli/execution.py +0 -636
  26. deepagents-cli/deepagents_cli/file_ops.py +0 -347
  27. deepagents-cli/deepagents_cli/input.py +0 -270
  28. deepagents-cli/deepagents_cli/main.py +0 -226
  29. deepagents-cli/deepagents_cli/py.typed +0 -0
  30. deepagents-cli/deepagents_cli/token_utils.py +0 -63
  31. deepagents-cli/deepagents_cli/tools.py +0 -140
  32. deepagents-cli/deepagents_cli/ui.py +0 -489
  33. deepagents-cli/tests/test_file_ops.py +0 -119
  34. deepagents-cli/tests/test_placeholder.py +0 -5
  35. {deepagents-0.2.5.dist-info → deepagents-0.2.7.dist-info}/WHEEL +0 -0
@@ -19,7 +19,15 @@ from langgraph.types import Command
19
19
  from typing_extensions import TypedDict
20
20
 
21
21
  from deepagents.backends import StateBackend
22
- from deepagents.backends.protocol import BackendFactory, BackendProtocol, EditResult, WriteResult
22
+
23
+ # Re-export type here for backwards compatibility
24
+ from deepagents.backends.protocol import BACKEND_TYPES as BACKEND_TYPES
25
+ from deepagents.backends.protocol import (
26
+ BackendProtocol,
27
+ EditResult,
28
+ SandboxBackendProtocol,
29
+ WriteResult,
30
+ )
23
31
  from deepagents.backends.utils import (
24
32
  format_content_with_line_numbers,
25
33
  format_grep_matches,
@@ -32,7 +40,6 @@ MAX_LINE_LENGTH = 2000
32
40
  LINE_NUMBER_WIDTH = 6
33
41
  DEFAULT_READ_OFFSET = 0
34
42
  DEFAULT_READ_LIMIT = 500
35
- BACKEND_TYPES = BackendProtocol | BackendFactory
36
43
 
37
44
 
38
45
  class FileData(TypedDict):
@@ -211,6 +218,50 @@ Examples:
211
218
  - Search Python files only: `grep(pattern="import", glob="*.py")`
212
219
  - Show matching lines: `grep(pattern="error", output_mode="content")`"""
213
220
 
221
+ EXECUTE_TOOL_DESCRIPTION = """Executes a given command in the sandbox environment with proper handling and security measures.
222
+
223
+ Before executing the command, please follow these steps:
224
+
225
+ 1. Directory Verification:
226
+ - If the command will create new directories or files, first use the ls tool to verify the parent directory exists and is the correct location
227
+ - For example, before running "mkdir foo/bar", first use ls to check that "foo" exists and is the intended parent directory
228
+
229
+ 2. Command Execution:
230
+ - Always quote file paths that contain spaces with double quotes (e.g., cd "path with spaces/file.txt")
231
+ - Examples of proper quoting:
232
+ - cd "/Users/name/My Documents" (correct)
233
+ - cd /Users/name/My Documents (incorrect - will fail)
234
+ - python "/path/with spaces/script.py" (correct)
235
+ - python /path/with spaces/script.py (incorrect - will fail)
236
+ - After ensuring proper quoting, execute the command
237
+ - Capture the output of the command
238
+
239
+ Usage notes:
240
+ - The command parameter is required
241
+ - Commands run in an isolated sandbox environment
242
+ - Returns combined stdout/stderr output with exit code
243
+ - If the output is very large, it may be truncated
244
+ - VERY IMPORTANT: You MUST avoid using search commands like find and grep. Instead use the grep, glob tools to search. You MUST avoid read tools like cat, head, tail, and use read_file to read files.
245
+ - When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings)
246
+ - Use '&&' when commands depend on each other (e.g., "mkdir dir && cd dir")
247
+ - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail
248
+ - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of cd
249
+
250
+ Examples:
251
+ Good examples:
252
+ - execute(command="pytest /foo/bar/tests")
253
+ - execute(command="python /path/to/script.py")
254
+ - execute(command="npm install && npm test")
255
+
256
+ Bad examples (avoid these):
257
+ - execute(command="cd /foo/bar && pytest tests") # Use absolute path instead
258
+ - execute(command="cat file.txt") # Use read_file tool instead
259
+ - execute(command="find . -name '*.py'") # Use glob tool instead
260
+ - execute(command="grep -r 'pattern' .") # Use grep tool instead
261
+
262
+ Note: This tool is only available if the backend supports execution (SandboxBackendProtocol).
263
+ If execution is not supported, the tool will return an error message."""
264
+
214
265
  FILESYSTEM_SYSTEM_PROMPT = """## Filesystem Tools `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep`
215
266
 
216
267
  You have access to a filesystem which you can interact with using these tools.
@@ -223,6 +274,13 @@ All file paths must start with a /.
223
274
  - glob: find files matching a pattern (e.g., "**/*.py")
224
275
  - grep: search for text within files"""
225
276
 
277
+ EXECUTION_SYSTEM_PROMPT = """## Execute Tool `execute`
278
+
279
+ You have access to an `execute` tool for running shell commands in a sandboxed environment.
280
+ Use this tool to run commands, scripts, tests, builds, and other shell operations.
281
+
282
+ - execute: run a shell command in the sandbox (returns output and exit code)"""
283
+
226
284
 
227
285
  def _get_backend(backend: BACKEND_TYPES, runtime: ToolRuntime) -> BackendProtocol:
228
286
  """Get the resolved backend instance from backend or factory.
@@ -440,6 +498,80 @@ def _grep_tool_generator(
440
498
  return grep
441
499
 
442
500
 
501
+ def _supports_execution(backend: BackendProtocol) -> bool:
502
+ """Check if a backend supports command execution.
503
+
504
+ For CompositeBackend, checks if the default backend supports execution.
505
+ For other backends, checks if they implement SandboxBackendProtocol.
506
+
507
+ Args:
508
+ backend: The backend to check.
509
+
510
+ Returns:
511
+ True if the backend supports execution, False otherwise.
512
+ """
513
+ # Import here to avoid circular dependency
514
+ from deepagents.backends.composite import CompositeBackend
515
+
516
+ # For CompositeBackend, check the default backend
517
+ if isinstance(backend, CompositeBackend):
518
+ return isinstance(backend.default, SandboxBackendProtocol)
519
+
520
+ # For other backends, use isinstance check
521
+ return isinstance(backend, SandboxBackendProtocol)
522
+
523
+
524
+ def _execute_tool_generator(
525
+ backend: BackendProtocol | Callable[[ToolRuntime], BackendProtocol],
526
+ custom_description: str | None = None,
527
+ ) -> BaseTool:
528
+ """Generate the execute tool for sandbox command execution.
529
+
530
+ Args:
531
+ backend: Backend to use for execution, or a factory function that takes runtime and returns a backend.
532
+ custom_description: Optional custom description for the tool.
533
+
534
+ Returns:
535
+ Configured execute tool that runs commands if backend supports SandboxBackendProtocol.
536
+ """
537
+ tool_description = custom_description or EXECUTE_TOOL_DESCRIPTION
538
+
539
+ @tool(description=tool_description)
540
+ def execute(
541
+ command: str,
542
+ runtime: ToolRuntime[None, FilesystemState],
543
+ ) -> str:
544
+ resolved_backend = _get_backend(backend, runtime)
545
+
546
+ # Runtime check - fail gracefully if not supported
547
+ if not _supports_execution(resolved_backend):
548
+ return (
549
+ "Error: Execution not available. This agent's backend "
550
+ "does not support command execution (SandboxBackendProtocol). "
551
+ "To use the execute tool, provide a backend that implements SandboxBackendProtocol."
552
+ )
553
+
554
+ try:
555
+ result = resolved_backend.execute(command)
556
+ except NotImplementedError as e:
557
+ # Handle case where execute() exists but raises NotImplementedError
558
+ return f"Error: Execution not available. {e}"
559
+
560
+ # Format output for LLM consumption
561
+ parts = [result.output]
562
+
563
+ if result.exit_code is not None:
564
+ status = "succeeded" if result.exit_code == 0 else "failed"
565
+ parts.append(f"\n[Command {status} with exit code {result.exit_code}]")
566
+
567
+ if result.truncated:
568
+ parts.append("\n[Output was truncated due to size limits]")
569
+
570
+ return "".join(parts)
571
+
572
+ return execute
573
+
574
+
443
575
  TOOL_GENERATORS = {
444
576
  "ls": _ls_tool_generator,
445
577
  "read_file": _read_file_tool_generator,
@@ -447,6 +579,7 @@ TOOL_GENERATORS = {
447
579
  "edit_file": _edit_file_tool_generator,
448
580
  "glob": _glob_tool_generator,
449
581
  "grep": _grep_tool_generator,
582
+ "execute": _execute_tool_generator,
450
583
  }
451
584
 
452
585
 
@@ -454,18 +587,19 @@ def _get_filesystem_tools(
454
587
  backend: BackendProtocol,
455
588
  custom_tool_descriptions: dict[str, str] | None = None,
456
589
  ) -> list[BaseTool]:
457
- """Get filesystem tools.
590
+ """Get filesystem and execution tools.
458
591
 
459
592
  Args:
460
- backend: Backend to use for file storage, or a factory function that takes runtime and returns a backend.
593
+ backend: Backend to use for file storage and optional execution, or a factory function that takes runtime and returns a backend.
461
594
  custom_tool_descriptions: Optional custom descriptions for tools.
462
595
 
463
596
  Returns:
464
- List of configured filesystem tools (ls, read_file, write_file, edit_file, glob, grep).
597
+ List of configured tools: ls, read_file, write_file, edit_file, glob, grep, execute.
465
598
  """
466
599
  if custom_tool_descriptions is None:
467
600
  custom_tool_descriptions = {}
468
601
  tools = []
602
+
469
603
  for tool_name, tool_generator in TOOL_GENERATORS.items():
470
604
  tool = tool_generator(backend, custom_tool_descriptions.get(tool_name))
471
605
  tools.append(tool)
@@ -483,16 +617,20 @@ Here are the first 10 lines of the result:
483
617
 
484
618
 
485
619
  class FilesystemMiddleware(AgentMiddleware):
486
- """Middleware for providing filesystem tools to an agent.
620
+ """Middleware for providing filesystem and optional execution tools to an agent.
487
621
 
488
- This middleware adds six filesystem tools to the agent: ls, read_file, write_file,
622
+ This middleware adds filesystem tools to the agent: ls, read_file, write_file,
489
623
  edit_file, glob, and grep. Files can be stored using any backend that implements
490
624
  the BackendProtocol.
491
625
 
626
+ If the backend implements SandboxBackendProtocol, an execute tool is also added
627
+ for running shell commands.
628
+
492
629
  Args:
493
- backend: Backend for file storage. If not provided, defaults to StateBackend
630
+ backend: Backend for file storage and optional execution. If not provided, defaults to StateBackend
494
631
  (ephemeral storage in agent state). For persistent storage or hybrid setups,
495
- use CompositeBackend with custom routes.
632
+ use CompositeBackend with custom routes. For execution support, use a backend
633
+ that implements SandboxBackendProtocol.
496
634
  system_prompt: Optional custom system prompt override.
497
635
  custom_tool_descriptions: Optional custom tool descriptions override.
498
636
  tool_token_limit_before_evict: Optional token limit before evicting a tool result to the filesystem.
@@ -500,15 +638,21 @@ class FilesystemMiddleware(AgentMiddleware):
500
638
  Example:
501
639
  ```python
502
640
  from deepagents.middleware.filesystem import FilesystemMiddleware
503
- from deepagents.memory.backends import StateBackend, StoreBackend, CompositeBackend
641
+ from deepagents.backends import StateBackend, StoreBackend, CompositeBackend
504
642
  from langchain.agents import create_agent
505
643
 
506
- # Ephemeral storage only (default)
644
+ # Ephemeral storage only (default, no execution)
507
645
  agent = create_agent(middleware=[FilesystemMiddleware()])
508
646
 
509
647
  # With hybrid storage (ephemeral + persistent /memories/)
510
648
  backend = CompositeBackend(default=StateBackend(), routes={"/memories/": StoreBackend()})
511
- agent = create_agent(middleware=[FilesystemMiddleware(memory_backend=backend)])
649
+ agent = create_agent(middleware=[FilesystemMiddleware(backend=backend)])
650
+
651
+ # With sandbox backend (supports execution)
652
+ from my_sandbox import DockerSandboxBackend
653
+
654
+ sandbox = DockerSandboxBackend(container_id="my-container")
655
+ agent = create_agent(middleware=[FilesystemMiddleware(backend=sandbox)])
512
656
  ```
513
657
  """
514
658
 
@@ -525,7 +669,8 @@ class FilesystemMiddleware(AgentMiddleware):
525
669
  """Initialize the filesystem middleware.
526
670
 
527
671
  Args:
528
- backend: Backend for file storage, or a factory callable. Defaults to StateBackend if not provided.
672
+ backend: Backend for file storage and optional execution, or a factory callable.
673
+ Defaults to StateBackend if not provided.
529
674
  system_prompt: Optional custom system prompt override.
530
675
  custom_tool_descriptions: Optional custom tool descriptions override.
531
676
  tool_token_limit_before_evict: Optional token limit before evicting a tool result to the filesystem.
@@ -535,8 +680,8 @@ class FilesystemMiddleware(AgentMiddleware):
535
680
  # Use provided backend or default to StateBackend factory
536
681
  self.backend = backend if backend is not None else (lambda rt: StateBackend(rt))
537
682
 
538
- # Set system prompt (allow full override)
539
- self.system_prompt = system_prompt if system_prompt is not None else FILESYSTEM_SYSTEM_PROMPT
683
+ # Set system prompt (allow full override or None to generate dynamically)
684
+ self._custom_system_prompt = system_prompt
540
685
 
541
686
  self.tools = _get_filesystem_tools(self.backend, custom_tool_descriptions)
542
687
 
@@ -558,7 +703,7 @@ class FilesystemMiddleware(AgentMiddleware):
558
703
  request: ModelRequest,
559
704
  handler: Callable[[ModelRequest], ModelResponse],
560
705
  ) -> ModelResponse:
561
- """Update the system prompt to include instructions on using the filesystem.
706
+ """Update the system prompt and filter tools based on backend capabilities.
562
707
 
563
708
  Args:
564
709
  request: The model request being processed.
@@ -567,8 +712,37 @@ class FilesystemMiddleware(AgentMiddleware):
567
712
  Returns:
568
713
  The model response from the handler.
569
714
  """
570
- if self.system_prompt is not None:
571
- request.system_prompt = request.system_prompt + "\n\n" + self.system_prompt if request.system_prompt else self.system_prompt
715
+ # Check if execute tool is present and if backend supports it
716
+ has_execute_tool = any((tool.name if hasattr(tool, "name") else tool.get("name")) == "execute" for tool in request.tools)
717
+
718
+ backend_supports_execution = False
719
+ if has_execute_tool:
720
+ # Resolve backend to check execution support
721
+ backend = self._get_backend(request.runtime)
722
+ backend_supports_execution = _supports_execution(backend)
723
+
724
+ # If execute tool exists but backend doesn't support it, filter it out
725
+ if not backend_supports_execution:
726
+ filtered_tools = [tool for tool in request.tools if (tool.name if hasattr(tool, "name") else tool.get("name")) != "execute"]
727
+ request = request.override(tools=filtered_tools)
728
+ has_execute_tool = False
729
+
730
+ # Use custom system prompt if provided, otherwise generate dynamically
731
+ if self._custom_system_prompt is not None:
732
+ system_prompt = self._custom_system_prompt
733
+ else:
734
+ # Build dynamic system prompt based on available tools
735
+ prompt_parts = [FILESYSTEM_SYSTEM_PROMPT]
736
+
737
+ # Add execution instructions if execute tool is available
738
+ if has_execute_tool and backend_supports_execution:
739
+ prompt_parts.append(EXECUTION_SYSTEM_PROMPT)
740
+
741
+ system_prompt = "\n\n".join(prompt_parts)
742
+
743
+ if system_prompt:
744
+ request = request.override(system_prompt=request.system_prompt + "\n\n" + system_prompt if request.system_prompt else system_prompt)
745
+
572
746
  return handler(request)
573
747
 
574
748
  async def awrap_model_call(
@@ -576,7 +750,7 @@ class FilesystemMiddleware(AgentMiddleware):
576
750
  request: ModelRequest,
577
751
  handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
578
752
  ) -> ModelResponse:
579
- """(async) Update the system prompt to include instructions on using the filesystem.
753
+ """(async) Update the system prompt and filter tools based on backend capabilities.
580
754
 
581
755
  Args:
582
756
  request: The model request being processed.
@@ -585,8 +759,37 @@ class FilesystemMiddleware(AgentMiddleware):
585
759
  Returns:
586
760
  The model response from the handler.
587
761
  """
588
- if self.system_prompt is not None:
589
- request.system_prompt = request.system_prompt + "\n\n" + self.system_prompt if request.system_prompt else self.system_prompt
762
+ # Check if execute tool is present and if backend supports it
763
+ has_execute_tool = any((tool.name if hasattr(tool, "name") else tool.get("name")) == "execute" for tool in request.tools)
764
+
765
+ backend_supports_execution = False
766
+ if has_execute_tool:
767
+ # Resolve backend to check execution support
768
+ backend = self._get_backend(request.runtime)
769
+ backend_supports_execution = _supports_execution(backend)
770
+
771
+ # If execute tool exists but backend doesn't support it, filter it out
772
+ if not backend_supports_execution:
773
+ filtered_tools = [tool for tool in request.tools if (tool.name if hasattr(tool, "name") else tool.get("name")) != "execute"]
774
+ request = request.override(tools=filtered_tools)
775
+ has_execute_tool = False
776
+
777
+ # Use custom system prompt if provided, otherwise generate dynamically
778
+ if self._custom_system_prompt is not None:
779
+ system_prompt = self._custom_system_prompt
780
+ else:
781
+ # Build dynamic system prompt based on available tools
782
+ prompt_parts = [FILESYSTEM_SYSTEM_PROMPT]
783
+
784
+ # Add execution instructions if execute tool is available
785
+ if has_execute_tool and backend_supports_execution:
786
+ prompt_parts.append(EXECUTION_SYSTEM_PROMPT)
787
+
788
+ system_prompt = "\n\n".join(prompt_parts)
789
+
790
+ if system_prompt:
791
+ request = request.override(system_prompt=request.system_prompt + "\n\n" + system_prompt if request.system_prompt else system_prompt)
792
+
590
793
  return await handler(request)
591
794
 
592
795
  def _process_large_message(
@@ -322,10 +322,7 @@ def _create_task_tool(
322
322
  )
323
323
 
324
324
  def _validate_and_prepare_state(subagent_type: str, description: str, runtime: ToolRuntime) -> tuple[Runnable, dict]:
325
- """Validate subagent type and prepare state for invocation."""
326
- if subagent_type not in subagent_graphs:
327
- msg = f"Error: invoked agent of type {subagent_type}, the only allowed types are {[f'`{k}`' for k in subagent_graphs]}"
328
- raise ValueError(msg)
325
+ """Prepare state for invocation."""
329
326
  subagent = subagent_graphs[subagent_type]
330
327
  # Create a new state dict to avoid mutating the original
331
328
  subagent_state = {k: v for k, v in runtime.state.items() if k not in _EXCLUDED_STATE_KEYS}
@@ -344,6 +341,9 @@ def _create_task_tool(
344
341
  subagent_type: str,
345
342
  runtime: ToolRuntime,
346
343
  ) -> str | Command:
344
+ if subagent_type not in subagent_graphs:
345
+ allowed_types = ", ".join([f"`{k}`" for k in subagent_graphs])
346
+ return f"We cannot invoke subagent {subagent_type} because it does not exist, the only allowed types are {allowed_types}"
347
347
  subagent, subagent_state = _validate_and_prepare_state(subagent_type, description, runtime)
348
348
  result = subagent.invoke(subagent_state)
349
349
  if not runtime.tool_call_id:
@@ -356,6 +356,9 @@ def _create_task_tool(
356
356
  subagent_type: str,
357
357
  runtime: ToolRuntime,
358
358
  ) -> str | Command:
359
+ if subagent_type not in subagent_graphs:
360
+ allowed_types = ", ".join([f"`{k}`" for k in subagent_graphs])
361
+ return f"We cannot invoke subagent {subagent_type} because it does not exist, the only allowed types are {allowed_types}"
359
362
  subagent, subagent_state = _validate_and_prepare_state(subagent_type, description, runtime)
360
363
  result = await subagent.ainvoke(subagent_state)
361
364
  if not runtime.tool_call_id:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents
3
- Version: 0.2.5
3
+ Version: 0.2.7
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,12 +11,13 @@ 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
- License-File: LICENSE
15
14
  Requires-Dist: langchain-anthropic<2.0.0,>=1.0.0
16
15
  Requires-Dist: langchain<2.0.0,>=1.0.2
17
16
  Requires-Dist: langchain-core<2.0.0,>=1.0.0
18
17
  Requires-Dist: wcmatch
19
- Dynamic: license-file
18
+ Requires-Dist: daytona>=0.113.0
19
+ Requires-Dist: runloop-api-client>=0.66.1
20
+ Requires-Dist: tavily>=1.1.0
20
21
 
21
22
  # 🧠🤖Deep Agents
22
23
 
@@ -347,7 +348,7 @@ Deep Agents are built with a modular middleware architecture. As a reminder, Dee
347
348
  - A filesystem for storing context and long-term memories
348
349
  - The ability to spawn subagents
349
350
 
350
- Each of these features is implemented as separate middleware. When you create a deep agent with `create_deep_agent`, we automatically attach **PlanningMiddleware**, **FilesystemMiddleware** and **SubAgentMiddleware** to your agent.
351
+ Each of these features is implemented as separate middleware. When you create a deep agent with `create_deep_agent`, we automatically attach **TodoListMiddleware**, **FilesystemMiddleware** and **SubAgentMiddleware** to your agent.
351
352
 
352
353
  Middleware is a composable concept, and you can choose to add as many or as few middleware to an agent depending on your use case. That means that you can also use any of the aforementioned middleware independently!
353
354
 
@@ -0,0 +1,18 @@
1
+ deepagents/__init__.py,sha256=9BVNn4lfF5N8l2KY8Ttxi82zO609I-fGqoSIF7DAxiU,342
2
+ deepagents/graph.py,sha256=c6ggWJIPaFOK2OCWxZdGEPDfuDvjdDuqHY06-bUiqPg,6379
3
+ deepagents/backends/__init__.py,sha256=BOKu2cQ1OdMyO_l2rLqZQiXppYFmQbx7OIQb7WYwvZc,457
4
+ deepagents/backends/composite.py,sha256=2BSyAurAt1FXV7iFyajzVaRZvjGkUPBybg7J8E6kRNE,9548
5
+ deepagents/backends/filesystem.py,sha256=SsVDx__j_AARIwRzaDuokbxbkquJ_Lw3Qi7dLWaqRUs,18674
6
+ deepagents/backends/protocol.py,sha256=Hi6u3MWIfMUGFWwnIFwvmwJbHYsx8IxeU2aaoP_9OMk,5831
7
+ deepagents/backends/sandbox.py,sha256=JueMe_2cZcA359JIqIi8kDUkmdtevC4VbKHw-fBPOWs,10125
8
+ deepagents/backends/state.py,sha256=ST_tUExPxArJaA3U8vc1dyzxuYl2BQH-HU7P0eu_Ty8,6518
9
+ deepagents/backends/store.py,sha256=f2LVSl65Dg-BZl-cY3pl3RqrUJCBMBm2kuAzZEODwsE,13098
10
+ deepagents/backends/utils.py,sha256=Iyk2jW-gfoLvMnz-W_2FRCoJW_j3r1zoumU9iww-jd0,13973
11
+ deepagents/middleware/__init__.py,sha256=_OGIHcHZ2pRD0gzUBS7R48agwI6P7-FCBKBgjyaWlsg,303
12
+ deepagents/middleware/filesystem.py,sha256=3PAetXqWy0i9bE6moM0FDZAEmjMm_M48B4AWWYl4Luk,37271
13
+ deepagents/middleware/patch_tool_calls.py,sha256=PdNhxPaQqwnFkhEAZEE2kEzadTNAOO3_iJRA30WqpGE,1981
14
+ deepagents/middleware/subagents.py,sha256=RbNpWLXC0Bhr0nUIs40whybNnzNkhxG9Fie7QKsICRk,23748
15
+ deepagents-0.2.7.dist-info/METADATA,sha256=H5FgpjWX5qzV5uHcpm0ba4F1Jwtw_Kvj79U9mdUR3Os,18887
16
+ deepagents-0.2.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ deepagents-0.2.7.dist-info/top_level.txt,sha256=drAzchOzPNePwpb3_pbPuvLuayXkN7SNqeIKMBWJoAo,11
18
+ deepagents-0.2.7.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ deepagents
@@ -1,86 +0,0 @@
1
- """Shell tool middleware that survives HITL pauses."""
2
-
3
- from __future__ import annotations
4
-
5
- from collections.abc import Awaitable, Callable
6
- from typing import Any, cast
7
-
8
- from langchain.agents.middleware.shell_tool import (
9
- ShellToolMiddleware,
10
- ShellToolState,
11
- _PersistentShellTool,
12
- _SessionResources,
13
- )
14
- from langchain.agents.middleware.types import AgentState
15
- from langchain.tools.tool_node import ToolCallRequest
16
- from langchain_core.messages import ToolMessage
17
- from langgraph.types import Command
18
-
19
-
20
- class ResumableShellToolMiddleware(ShellToolMiddleware):
21
- """Shell middleware that recreates session resources after human interrupts.
22
-
23
- ``ShellToolMiddleware`` stores its session handle in middleware state using an
24
- ``UntrackedValue``. When a run pauses for human approval, that attribute is not
25
- checkpointed. Upon resuming, LangGraph restores the state without the shell
26
- resources, so the next tool execution fails with
27
- ``Shell session resources are unavailable``.
28
-
29
- This subclass lazily recreates the shell session the first time a resumed run
30
- touches the shell tool again and only performs shutdown when a session is
31
- actually active. This keeps behaviour identical for uninterrupted runs while
32
- allowing HITL pauses to succeed.
33
- """
34
-
35
- def wrap_tool_call(
36
- self,
37
- request: ToolCallRequest,
38
- handler: Callable[[ToolCallRequest], ToolMessage | Command],
39
- ) -> ToolMessage | Command:
40
- if isinstance(request.tool, _PersistentShellTool):
41
- resources = self._get_or_create_resources(request.state)
42
- return self._run_shell_tool(
43
- resources,
44
- request.tool_call["args"],
45
- tool_call_id=request.tool_call.get("id"),
46
- )
47
- return super().wrap_tool_call(request, handler)
48
-
49
- async def awrap_tool_call(
50
- self,
51
- request: ToolCallRequest,
52
- handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]],
53
- ) -> ToolMessage | Command:
54
- if isinstance(request.tool, _PersistentShellTool):
55
- resources = self._get_or_create_resources(request.state)
56
- return self._run_shell_tool(
57
- resources,
58
- request.tool_call["args"],
59
- tool_call_id=request.tool_call.get("id"),
60
- )
61
- return await super().awrap_tool_call(request, handler)
62
-
63
- def after_agent(self, state: ShellToolState, runtime) -> None: # type: ignore[override]
64
- if self._has_resources(state):
65
- super().after_agent(state, runtime)
66
-
67
- async def aafter_agent(self, state: ShellToolState, runtime) -> None: # type: ignore[override]
68
- if self._has_resources(state):
69
- await super().aafter_agent(state, runtime)
70
-
71
- @staticmethod
72
- def _has_resources(state: AgentState) -> bool:
73
- resources = state.get("shell_session_resources")
74
- return isinstance(resources, _SessionResources)
75
-
76
- def _get_or_create_resources(self, state: AgentState) -> _SessionResources:
77
- resources = state.get("shell_session_resources")
78
- if isinstance(resources, _SessionResources):
79
- return resources
80
-
81
- new_resources = self._create_resources()
82
- cast("dict[str, Any]", state)["shell_session_resources"] = new_resources
83
- return new_resources
84
-
85
-
86
- __all__ = ["ResumableShellToolMiddleware"]
@@ -1,38 +0,0 @@
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,,
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Harrison Chase
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,2 +0,0 @@
1
- deepagents
2
- deepagents-cli
deepagents-cli/README.md DELETED
@@ -1,3 +0,0 @@
1
- # deepagents cli
2
-
3
- This is the CLI for deepagents