deepagents-code 0.1.0__tar.gz → 0.1.1__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.
Files changed (239) hide show
  1. deepagents_code-0.1.1/CHANGELOG.md +17 -0
  2. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/PKG-INFO +4 -4
  3. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/README.md +3 -3
  4. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_version.py +2 -2
  5. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/app.tcss +2 -2
  6. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/integrations/sandbox_factory.py +1 -1
  7. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/main.py +1 -1
  8. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/model_config.py +1 -1
  9. deepagents_code-0.1.1/deepagents_code/tools.py +406 -0
  10. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/pyproject.toml +1 -1
  11. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/scripts/install.sh +2 -2
  12. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_main.py +1 -1
  13. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_messages.py +1 -1
  14. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_sandbox_factory.py +1 -1
  15. deepagents_code-0.1.1/tests/unit_tests/tools/test_fetch_url.py +474 -0
  16. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/uv.lock +4 -4
  17. deepagents_code-0.1.0/CHANGELOG.md +0 -10
  18. deepagents_code-0.1.0/deepagents_code/tools.py +0 -159
  19. deepagents_code-0.1.0/tests/unit_tests/tools/test_fetch_url.py +0 -72
  20. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/.gitignore +0 -0
  21. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/DEV.md +0 -0
  22. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/Makefile +0 -0
  23. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/THREAT_MODEL.md +0 -0
  24. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/__init__.py +0 -0
  25. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/__main__.py +0 -0
  26. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_ask_user_types.py +0 -0
  27. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_cli_context.py +0 -0
  28. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_constants.py +0 -0
  29. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_debug.py +0 -0
  30. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_env_vars.py +0 -0
  31. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_git.py +0 -0
  32. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_server_config.py +0 -0
  33. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_session_stats.py +0 -0
  34. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_testing_models.py +0 -0
  35. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/_textual_patches.py +0 -0
  36. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/agent.py +0 -0
  37. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/app.py +0 -0
  38. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/ask_user.py +0 -0
  39. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/auth_store.py +0 -0
  40. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/built_in_skills/__init__.py +0 -0
  41. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
  42. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
  43. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  44. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  45. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/clipboard.py +0 -0
  46. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/command_registry.py +0 -0
  47. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/config.py +0 -0
  48. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/configurable_model.py +0 -0
  49. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/default_agent_prompt.md +0 -0
  50. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/editor.py +0 -0
  51. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/event_bus.py +0 -0
  52. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/extras_info.py +0 -0
  53. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/file_ops.py +0 -0
  54. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/formatting.py +0 -0
  55. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/hooks.py +0 -0
  56. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/input.py +0 -0
  57. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/integrations/__init__.py +0 -0
  58. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/integrations/sandbox_provider.py +0 -0
  59. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/iterm_cursor_guide.py +0 -0
  60. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/local_context.py +0 -0
  61. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_auth.py +0 -0
  62. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_commands.py +0 -0
  63. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_providers/__init__.py +0 -0
  64. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_providers/_registry.py +0 -0
  65. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_providers/base.py +0 -0
  66. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_providers/github.py +0 -0
  67. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_providers/slack.py +0 -0
  68. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_tools.py +0 -0
  69. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/mcp_trust.py +0 -0
  70. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/media_utils.py +0 -0
  71. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/non_interactive.py +0 -0
  72. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/notifications.py +0 -0
  73. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/offload.py +0 -0
  74. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/onboarding.py +0 -0
  75. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/output.py +0 -0
  76. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/project_utils.py +0 -0
  77. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/py.typed +0 -0
  78. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/remote_client.py +0 -0
  79. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/server.py +0 -0
  80. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/server_graph.py +0 -0
  81. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/server_manager.py +0 -0
  82. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/sessions.py +0 -0
  83. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/skills/__init__.py +0 -0
  84. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/skills/commands.py +0 -0
  85. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/skills/invocation.py +0 -0
  86. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/skills/load.py +0 -0
  87. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/state_migration.py +0 -0
  88. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/subagents.py +0 -0
  89. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/system_prompt.md +0 -0
  90. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/terminal_capabilities.py +0 -0
  91. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/terminal_escape.py +0 -0
  92. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/textual_adapter.py +0 -0
  93. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/theme.py +0 -0
  94. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/token_state.py +0 -0
  95. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/tool_display.py +0 -0
  96. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/ui.py +0 -0
  97. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/unicode_security.py +0 -0
  98. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/update_check.py +0 -0
  99. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/__init__.py +0 -0
  100. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/_links.py +0 -0
  101. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/agent_selector.py +0 -0
  102. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/approval.py +0 -0
  103. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/ask_user.py +0 -0
  104. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/auth.py +0 -0
  105. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/autocomplete.py +0 -0
  106. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/chat_input.py +0 -0
  107. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/diff.py +0 -0
  108. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/history.py +0 -0
  109. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/launch_init.py +0 -0
  110. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/loading.py +0 -0
  111. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/mcp_viewer.py +0 -0
  112. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/message_store.py +0 -0
  113. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/messages.py +0 -0
  114. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/model_selector.py +0 -0
  115. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/notification_center.py +0 -0
  116. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/notification_detail.py +0 -0
  117. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/notification_settings.py +0 -0
  118. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/status.py +0 -0
  119. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/theme_selector.py +0 -0
  120. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/thread_selector.py +0 -0
  121. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/tool_renderers.py +0 -0
  122. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/tool_widgets.py +0 -0
  123. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/update_available.py +0 -0
  124. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/update_progress.py +0 -0
  125. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/deepagents_code/widgets/welcome.py +0 -0
  126. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/examples/skills/arxiv-search/SKILL.md +0 -0
  127. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  128. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/examples/skills/langgraph-docs/SKILL.md +0 -0
  129. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/examples/skills/skill-creator/SKILL.md +0 -0
  130. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  131. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  132. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/examples/skills/web-research/SKILL.md +0 -0
  133. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/images/tui.png +0 -0
  134. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/scripts/check_imports.py +0 -0
  135. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/scripts/debug_server.sh +0 -0
  136. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/README.md +0 -0
  137. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/__init__.py +0 -0
  138. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/benchmarks/__init__.py +0 -0
  139. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  140. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  141. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/conftest.py +0 -0
  142. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/test_acp_mode.py +0 -0
  143. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/test_compact_resume.py +0 -0
  144. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/test_sandbox_factory.py +0 -0
  145. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/integration_tests/test_sandbox_operations.py +0 -0
  146. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/__init__.py +0 -0
  147. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/conftest.py +0 -0
  148. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/skills/__init__.py +0 -0
  149. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/skills/test_commands.py +0 -0
  150. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/skills/test_load.py +0 -0
  151. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/skills/test_skills_json.py +0 -0
  152. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_agent.py +0 -0
  153. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_agent_friendly.py +0 -0
  154. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_agent_selector.py +0 -0
  155. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_app.py +0 -0
  156. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_approval.py +0 -0
  157. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_args.py +0 -0
  158. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_ask_user.py +0 -0
  159. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  160. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_auth_store.py +0 -0
  161. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_auth_widgets.py +0 -0
  162. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_autocomplete.py +0 -0
  163. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_charset.py +0 -0
  164. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_chat_input.py +0 -0
  165. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_clipboard.py +0 -0
  166. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_command_registry.py +0 -0
  167. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_compact_tool.py +0 -0
  168. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_config.py +0 -0
  169. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_configurable_model.py +0 -0
  170. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_debug.py +0 -0
  171. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_editor.py +0 -0
  172. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_end_to_end.py +0 -0
  173. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_env_vars.py +0 -0
  174. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_event_bus.py +0 -0
  175. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_exception_handling.py +0 -0
  176. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_extras_info.py +0 -0
  177. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_file_ops.py +0 -0
  178. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_formatting.py +0 -0
  179. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_git.py +0 -0
  180. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_history.py +0 -0
  181. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_hooks.py +0 -0
  182. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_imports.py +0 -0
  183. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_input_parsing.py +0 -0
  184. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
  185. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_launch_init.py +0 -0
  186. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_links.py +0 -0
  187. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_loading.py +0 -0
  188. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_local_context.py +0 -0
  189. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_main_acp_mode.py +0 -0
  190. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_main_args.py +0 -0
  191. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_mcp_auth.py +0 -0
  192. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_mcp_commands.py +0 -0
  193. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_mcp_tools.py +0 -0
  194. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_mcp_trust.py +0 -0
  195. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_mcp_viewer.py +0 -0
  196. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_media_utils.py +0 -0
  197. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_message_store.py +0 -0
  198. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_model_config.py +0 -0
  199. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_model_selector.py +0 -0
  200. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_model_switch.py +0 -0
  201. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_non_interactive.py +0 -0
  202. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_notification_center.py +0 -0
  203. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_notification_detail.py +0 -0
  204. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_notifications.py +0 -0
  205. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_offload.py +0 -0
  206. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  207. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_onboarding.py +0 -0
  208. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_output.py +0 -0
  209. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_reload.py +0 -0
  210. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_remote_client.py +0 -0
  211. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_server.py +0 -0
  212. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_server_config.py +0 -0
  213. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_server_graph.py +0 -0
  214. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_server_helpers.py +0 -0
  215. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_server_manager.py +0 -0
  216. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_session_stats.py +0 -0
  217. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_sessions.py +0 -0
  218. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_shell_allow_list.py +0 -0
  219. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_skill_invocation.py +0 -0
  220. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_startup_fast_paths.py +0 -0
  221. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_state_migration.py +0 -0
  222. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_status.py +0 -0
  223. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_subagents.py +0 -0
  224. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  225. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_terminal_escape.py +0 -0
  226. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_textual_adapter.py +0 -0
  227. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_textual_patches.py +0 -0
  228. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_theme.py +0 -0
  229. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_thread_selector.py +0 -0
  230. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_token_tracker.py +0 -0
  231. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_tool_display.py +0 -0
  232. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_ui.py +0 -0
  233. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_unicode_security.py +0 -0
  234. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_update_available.py +0 -0
  235. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_update_check.py +0 -0
  236. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_update_progress.py +0 -0
  237. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_version.py +0 -0
  238. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/test_welcome.py +0 -0
  239. {deepagents_code-0.1.0 → deepagents_code-0.1.1}/tests/unit_tests/tools/__init__.py +0 -0
@@ -0,0 +1,17 @@
1
+ # Deep Agents Code Changelog
2
+
3
+ ## [0.1.1](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.0...deepagents-code==0.1.1) (2026-05-16)
4
+
5
+ ### Bug Fixes
6
+
7
+ * Correct LangSmith sandbox working directory ([#3415](https://github.com/langchain-ai/deepagents/issues/3415)) ([b0e8d83](https://github.com/langchain-ai/deepagents/commit/b0e8d83f97a2a698268173a839000c84e8368324))
8
+ * Guard `fetch_url` against SSRF ([#3411](https://github.com/langchain-ai/deepagents/issues/3411)) ([54d8521](https://github.com/langchain-ai/deepagents/commit/54d8521976940dfe147ead4b56565360241335be))
9
+
10
+ ## [0.1.0](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.0.1...deepagents-code==0.1.0) (2026-05-12)
11
+
12
+ Hello world! Ported from `libs/cli`.
13
+
14
+ ---
15
+
16
+ `deepagents-code` was forked from `deepagents-cli` at v0.1.0 (2026-05-12).
17
+ For history prior to the fork, see [the `deepagents-cli` changelog](https://github.com/langchain-ai/deepagents/blob/main/libs/cli/CHANGELOG.md).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents-code
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Terminal interface for Deep Agents - interactive AI agent with file operations, shell access, and sub-agent capabilities.
5
5
  Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
6
6
  Project-URL: Documentation, https://reference.langchain.com/python/deepagents/
@@ -145,13 +145,13 @@ Description-Content-Type: text/markdown
145
145
  ## Quick Install
146
146
 
147
147
  ```bash
148
- curl -LsSf https://langch.in/gh-da-cli | bash
148
+ curl -LsSf https://langch.in/dcode | bash
149
149
  ```
150
150
 
151
151
  ```bash
152
152
  # With model provider extras
153
153
  # OpenAI, Anthropic, and Gemini are included by default
154
- DEEPAGENTS_EXTRAS="nvidia,ollama" curl -LsSf https://langch.in/gh-da-cli | bash
154
+ DEEPAGENTS_EXTRAS="nvidia,ollama" curl -LsSf https://langch.in/dcode | bash
155
155
  ```
156
156
 
157
157
  Or install directly with `uv`:
@@ -184,7 +184,7 @@ The fastest way to start using Deep Agents. `deepagents-code` is a pre-built cod
184
184
 
185
185
  ## 📖 Resources
186
186
 
187
- - **[CLI Documentation](https://docs.langchain.com/oss/python/deepagents/cli/overview)**
187
+ - **[Documentation](https://docs.langchain.com/oss/python/deepagents/code/overview)**
188
188
  - **[Changelog](https://github.com/langchain-ai/deepagents/blob/main/libs/code/CHANGELOG.md)**
189
189
  - **[Source code](https://github.com/langchain-ai/deepagents/tree/main/libs/code)**
190
190
  - **[Deep Agents SDK](https://github.com/langchain-ai/deepagents)** — underlying agent harness
@@ -12,13 +12,13 @@
12
12
  ## Quick Install
13
13
 
14
14
  ```bash
15
- curl -LsSf https://langch.in/gh-da-cli | bash
15
+ curl -LsSf https://langch.in/dcode | bash
16
16
  ```
17
17
 
18
18
  ```bash
19
19
  # With model provider extras
20
20
  # OpenAI, Anthropic, and Gemini are included by default
21
- DEEPAGENTS_EXTRAS="nvidia,ollama" curl -LsSf https://langch.in/gh-da-cli | bash
21
+ DEEPAGENTS_EXTRAS="nvidia,ollama" curl -LsSf https://langch.in/dcode | bash
22
22
  ```
23
23
 
24
24
  Or install directly with `uv`:
@@ -51,7 +51,7 @@ The fastest way to start using Deep Agents. `deepagents-code` is a pre-built cod
51
51
 
52
52
  ## 📖 Resources
53
53
 
54
- - **[CLI Documentation](https://docs.langchain.com/oss/python/deepagents/cli/overview)**
54
+ - **[Documentation](https://docs.langchain.com/oss/python/deepagents/code/overview)**
55
55
  - **[Changelog](https://github.com/langchain-ai/deepagents/blob/main/libs/code/CHANGELOG.md)**
56
56
  - **[Source code](https://github.com/langchain-ai/deepagents/tree/main/libs/code)**
57
57
  - **[Deep Agents SDK](https://github.com/langchain-ai/deepagents)** — underlying agent harness
@@ -2,9 +2,9 @@
2
2
 
3
3
  # Keep the `x-release-please-version` annotation — release-please uses it to
4
4
  # bump `__version__` in sync with `pyproject.toml` on every release PR.
5
- __version__ = "0.1.0" # x-release-please-version
5
+ __version__ = "0.1.1" # x-release-please-version
6
6
 
7
- DOCS_URL = "https://docs.langchain.com/oss/python/deepagents/cli"
7
+ DOCS_URL = "https://docs.langchain.com/oss/python/deepagents/code"
8
8
  """URL for `deepagents-code` documentation."""
9
9
 
10
10
  PYPI_URL = "https://pypi.org/pypi/deepagents-code/json"
@@ -24,7 +24,7 @@ Screen {
24
24
  /* Chat area - main scrollable messages area */
25
25
  #chat {
26
26
  height: 1fr;
27
- padding: 1 2;
27
+ padding: 1 2 0 2;
28
28
  background: $background;
29
29
  }
30
30
 
@@ -44,7 +44,7 @@ Screen {
44
44
  /* Bottom app container - holds ChatInput (now inside scroll) */
45
45
  #bottom-app-container {
46
46
  height: auto;
47
- margin-top: 1;
47
+ margin-top: 0;
48
48
  }
49
49
 
50
50
  /* Input area */
@@ -73,7 +73,7 @@ def _run_sandbox_setup(backend: SandboxBackendProtocol, setup_script_path: str)
73
73
  _PROVIDER_TO_WORKING_DIR = {
74
74
  "agentcore": "/tmp", # noqa: S108 # AgentCore Code Interpreter working directory
75
75
  "daytona": "/home/daytona",
76
- "langsmith": "/tmp", # noqa: S108 # LangSmith sandbox working directory
76
+ "langsmith": "/root", # `$HOME` in the LangSmith sandbox
77
77
  "modal": "/workspace",
78
78
  "runloop": "/home/user",
79
79
  }
@@ -1521,7 +1521,7 @@ def _check_mcp_project_trust(*, trust_flag: bool = False) -> bool | None:
1521
1521
  from rich.console import Console as _Console
1522
1522
 
1523
1523
  docs_url = (
1524
- "https://docs.langchain.com/oss/python/deepagents/cli/"
1524
+ "https://docs.langchain.com/oss/python/deepagents/code/"
1525
1525
  "mcp-tools#project-level-trust"
1526
1526
  )
1527
1527
  prompt_console = _Console(stderr=True)
@@ -94,7 +94,7 @@ def resolve_env_var(name: str) -> str | None:
94
94
 
95
95
 
96
96
  PROVIDERS_DOCS_URL = (
97
- "https://docs.langchain.com/oss/python/deepagents/cli/providers#provider-reference"
97
+ "https://docs.langchain.com/oss/python/deepagents/code/providers#provider-reference"
98
98
  )
99
99
  """Public docs page for configuring model providers.
100
100
 
@@ -0,0 +1,406 @@
1
+ """Custom tools for the agent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import contextlib
6
+ import ipaddress
7
+ import logging
8
+ import socket
9
+ import threading
10
+ from typing import TYPE_CHECKING, Any, Literal
11
+ from urllib.parse import urljoin, urlparse
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Iterator
15
+
16
+ from tavily import TavilyClient
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ _UNSET = object()
21
+ _tavily_client: TavilyClient | object | None = _UNSET
22
+
23
+ _ALLOWED_URL_SCHEMES = frozenset({"http", "https"})
24
+ _MAX_FETCH_REDIRECTS = 5
25
+
26
+ # Module-level lock guarding the urllib3 connection-factory monkeypatch used by
27
+ # `_pinned_dns`. The patch is process-global, so serializing fetches keeps
28
+ # concurrent calls from clobbering each other's pinned IP set.
29
+ _dns_pin_lock = threading.Lock()
30
+
31
+
32
+ class _UrlValidationError(ValueError):
33
+ """Raised by `_validate_url` for scheme/DNS/SSRF-blocked URLs.
34
+
35
+ Distinguishes intentional SSRF-guard rejections from incidental
36
+ `ValueError`s raised elsewhere in the fetch path (e.g., markdown
37
+ conversion).
38
+ """
39
+
40
+
41
+ def _is_blocked_ip(ip: ipaddress.IPv4Address | ipaddress.IPv6Address) -> bool:
42
+ """Return True if `ip` belongs to a non-publicly-routable range.
43
+
44
+ Rejects: private (RFC1918/ULA), loopback, link-local (including cloud
45
+ IMDS at `169.254.169.254`), reserved, multicast, unspecified
46
+ (`0.0.0.0`/`::`), and anything `ipaddress` does not consider globally
47
+ routable (catches benchmarking, documentation, and similar ranges the
48
+ explicit predicates miss).
49
+
50
+ IPv4-mapped IPv6 (`::ffff:a.b.c.d`) and 6to4 (`2002::/16`) are unwrapped
51
+ to their underlying IPv4 address before the checks so that private
52
+ space tunneled inside an IPv6 wrapper is still caught — e.g.,
53
+ `::ffff:127.0.0.1` and `2002:a9fe:a9fe::1` (6to4 over IMDS) both
54
+ evaluate as blocked.
55
+ """
56
+ if isinstance(ip, ipaddress.IPv6Address):
57
+ if ip.ipv4_mapped is not None:
58
+ ip = ip.ipv4_mapped
59
+ elif ip.sixtofour is not None:
60
+ ip = ip.sixtofour
61
+ return (
62
+ not ip.is_global
63
+ or ip.is_private
64
+ or ip.is_loopback
65
+ or ip.is_link_local
66
+ or ip.is_reserved
67
+ or ip.is_multicast
68
+ or ip.is_unspecified
69
+ )
70
+
71
+
72
+ def _validate_url(url: str) -> list[str]:
73
+ """Reject URLs that target private/internal/metadata addresses.
74
+
75
+ Resolves the URL's hostname and rejects any URL whose hostname resolves
76
+ to a private, loopback, link-local (includes cloud IMDS at
77
+ `169.254.169.254`), reserved, multicast, or unspecified IP — including
78
+ such addresses wrapped in IPv4-mapped IPv6 (`::ffff:...`) or 6to4
79
+ (`2002::/16`). This is the SSRF guard required because the URL is
80
+ supplied by an LLM agent and may originate from prompt-injected content.
81
+
82
+ Note:
83
+ This function resolves DNS once. The HTTP client must be pinned to
84
+ the returned IP list (see `_pinned_dns`) to close the TOCTOU window
85
+ against attacker-controlled DNS (rebinding).
86
+
87
+ Args:
88
+ url: Candidate URL to validate.
89
+
90
+ Returns:
91
+ The list of validated IP strings the hostname resolves to.
92
+
93
+ Callers should pin the outgoing connection to one of these IPs.
94
+
95
+ Raises:
96
+ _UrlValidationError: If the URL is malformed, uses a disallowed
97
+ scheme, fails DNS resolution, or resolves to a blocked address.
98
+ """
99
+ parsed = urlparse(url)
100
+ if parsed.scheme not in _ALLOWED_URL_SCHEMES:
101
+ msg = f"URL scheme not allowed: {parsed.scheme!r} (must be http or https)"
102
+ raise _UrlValidationError(msg)
103
+
104
+ hostname = parsed.hostname
105
+ if not hostname:
106
+ msg = "URL is missing a hostname"
107
+ raise _UrlValidationError(msg)
108
+
109
+ try:
110
+ encoded_hostname = hostname.encode("idna").decode("ascii")
111
+ except UnicodeError as exc:
112
+ msg = f"Could not encode hostname {hostname!r} as IDNA: {exc}"
113
+ raise _UrlValidationError(msg) from exc
114
+
115
+ try:
116
+ infos = socket.getaddrinfo(
117
+ encoded_hostname,
118
+ None,
119
+ type=socket.SOCK_STREAM,
120
+ proto=socket.IPPROTO_TCP,
121
+ )
122
+ except socket.gaierror as exc:
123
+ msg = f"Could not resolve hostname {hostname!r}: {exc}"
124
+ raise _UrlValidationError(msg) from exc
125
+
126
+ validated_ips: list[str] = []
127
+ for info in infos:
128
+ # `sockaddr[0]` may include an IPv6 scope id (`fe80::1%eth0`); strip
129
+ # it before parsing so `ipaddress.ip_address` never raises.
130
+ raw_ip = str(info[4][0]).split("%", 1)[0]
131
+ ip = ipaddress.ip_address(raw_ip)
132
+ if _is_blocked_ip(ip):
133
+ logger.warning(
134
+ "SSRF guard blocked URL %r: hostname %r resolves to %s",
135
+ url,
136
+ hostname,
137
+ ip,
138
+ )
139
+ msg = (
140
+ f"URL hostname {hostname!r} resolves to blocked address {ip} "
141
+ "(private, loopback, link-local, reserved, or non-global range)"
142
+ )
143
+ raise _UrlValidationError(msg)
144
+ validated_ips.append(raw_ip)
145
+
146
+ if not validated_ips:
147
+ msg = f"Hostname {hostname!r} resolved to no addresses"
148
+ raise _UrlValidationError(msg)
149
+
150
+ return validated_ips
151
+
152
+
153
+ @contextlib.contextmanager
154
+ def _pinned_dns(hostname: str, allowed_ips: list[str]) -> Iterator[None]:
155
+ """Force outgoing urllib3 connections for `hostname` to use `allowed_ips`.
156
+
157
+ Patches `urllib3.util.connection.create_connection` for the duration of
158
+ the context so that `requests` cannot re-resolve `hostname` to a
159
+ different IP than the one `_validate_url` vetted (defends against DNS
160
+ rebinding TOCTOU). The patch is process-global, so the module lock
161
+ serializes concurrent fetches.
162
+
163
+ Args:
164
+ hostname: The exact hostname (already IDNA-encoded by the caller)
165
+ whose resolution must be pinned.
166
+ allowed_ips: The IPs `_validate_url` confirmed are safe to connect
167
+ to. Tried in order; the first that accepts the connection wins.
168
+ """
169
+ from urllib3.util import connection as urllib3_connection
170
+
171
+ with _dns_pin_lock:
172
+ original = urllib3_connection.create_connection
173
+
174
+ def patched(
175
+ address: tuple[str, int], *args: Any, **kwargs: Any
176
+ ) -> socket.socket:
177
+ host, port = address[0], address[1]
178
+ if host != hostname:
179
+ return original(address, *args, **kwargs)
180
+ last_exc: OSError | None = None
181
+ for ip in allowed_ips:
182
+ try:
183
+ return original((ip, port), *args, **kwargs)
184
+ except OSError as exc:
185
+ last_exc = exc
186
+ assert last_exc is not None # noqa: S101 # loop body guarantees this
187
+ raise last_exc
188
+
189
+ urllib3_connection.create_connection = patched # type: ignore[assignment] # signature matches at runtime
190
+ try:
191
+ yield
192
+ finally:
193
+ urllib3_connection.create_connection = original
194
+
195
+
196
+ def _get_tavily_client() -> TavilyClient | None:
197
+ """Get or initialize the lazy Tavily client singleton.
198
+
199
+ Returns:
200
+ TavilyClient instance, or None if API key is not configured.
201
+ """
202
+ global _tavily_client # noqa: PLW0603 # Module-level cache requires global statement
203
+ if _tavily_client is not _UNSET:
204
+ return _tavily_client # type: ignore[return-value] # narrowed by sentinel check
205
+
206
+ from deepagents_code.config import settings
207
+
208
+ if settings.has_tavily:
209
+ from tavily import TavilyClient as _TavilyClient
210
+
211
+ _tavily_client = _TavilyClient(api_key=settings.tavily_api_key)
212
+ else:
213
+ _tavily_client = None
214
+ return _tavily_client
215
+
216
+
217
+ def web_search( # noqa: ANN201 # Return type depends on dynamic tool configuration
218
+ query: str,
219
+ max_results: int = 5,
220
+ topic: Literal["general", "news", "finance"] = "general",
221
+ include_raw_content: bool = False,
222
+ ):
223
+ """Search the web using Tavily for current information and documentation.
224
+
225
+ This tool searches the web and returns relevant results. After receiving results,
226
+ you MUST synthesize the information into a natural, helpful response for the user.
227
+
228
+ Args:
229
+ query: The search query (be specific and detailed)
230
+ max_results: Number of results to return (default: 5)
231
+ topic: Search topic type - "general" for most queries, "news" for current events
232
+ include_raw_content: Include full page content (warning: uses more tokens)
233
+
234
+ Returns:
235
+ Dictionary containing:
236
+ - results: List of search results, each with:
237
+ - title: Page title
238
+ - url: Page URL
239
+ - content: Relevant excerpt from the page
240
+ - score: Relevance score (0-1)
241
+ - query: The original search query
242
+
243
+ IMPORTANT: After using this tool:
244
+ 1. Read through the 'content' field of each result
245
+ 2. Extract relevant information that answers the user's question
246
+ 3. Synthesize this into a clear, natural language response
247
+ 4. Cite sources by mentioning the page titles or URLs
248
+ 5. NEVER show the raw JSON to the user - always provide a formatted response
249
+ """
250
+ try:
251
+ import requests
252
+ from tavily import (
253
+ BadRequestError,
254
+ InvalidAPIKeyError,
255
+ MissingAPIKeyError,
256
+ UsageLimitExceededError,
257
+ )
258
+ from tavily.errors import ForbiddenError, TimeoutError as TavilyTimeoutError
259
+ except ImportError as exc:
260
+ return {"error": f"Required package not installed: {exc.name}."}
261
+
262
+ client = _get_tavily_client()
263
+ if client is None:
264
+ return {
265
+ "error": "Tavily API key not configured. "
266
+ "Please set TAVILY_API_KEY environment variable.",
267
+ "query": query,
268
+ }
269
+
270
+ try:
271
+ return client.search(
272
+ query,
273
+ max_results=max_results,
274
+ include_raw_content=include_raw_content,
275
+ topic=topic,
276
+ )
277
+ except (
278
+ requests.exceptions.RequestException,
279
+ ValueError,
280
+ TypeError,
281
+ # Tavily-specific exceptions
282
+ BadRequestError,
283
+ ForbiddenError,
284
+ InvalidAPIKeyError,
285
+ MissingAPIKeyError,
286
+ TavilyTimeoutError,
287
+ UsageLimitExceededError,
288
+ ) as e:
289
+ return {"error": f"Web search error: {e!s}", "query": query}
290
+
291
+
292
+ def fetch_url(url: str, timeout: int = 30) -> dict[str, Any]:
293
+ """Fetch content from a URL and convert HTML to markdown format.
294
+
295
+ This tool fetches web page content and converts it to clean markdown text,
296
+ making it easy to read and process HTML content. After receiving the markdown,
297
+ you MUST synthesize the information into a natural, helpful response for the user.
298
+
299
+ Args:
300
+ url: The URL to fetch (must be a valid HTTP/HTTPS URL)
301
+ timeout: Request timeout in seconds (default: 30)
302
+
303
+ Returns:
304
+ Dictionary containing:
305
+ - success: Whether the request succeeded
306
+ - url: The final URL after redirects
307
+ - markdown_content: The page content converted to markdown
308
+ - status_code: HTTP status code
309
+ - content_length: Length of the markdown content in characters
310
+
311
+ IMPORTANT: After using this tool:
312
+ 1. Read through the markdown content
313
+ 2. Extract relevant information that answers the user's question
314
+ 3. Synthesize this into a clear, natural language response
315
+ 4. NEVER show the raw markdown to the user unless specifically requested
316
+ """
317
+ try:
318
+ import requests
319
+ from markdownify import markdownify
320
+ except ImportError as exc:
321
+ return {"error": f"Required package not installed: {exc.name}."}
322
+
323
+ try:
324
+ response = _fetch_with_redirects(url, timeout=timeout)
325
+ except _UrlValidationError as e:
326
+ return {
327
+ "error": f"Fetch URL error: {e!s}",
328
+ "url": url,
329
+ "category": "validation",
330
+ }
331
+ except requests.exceptions.TooManyRedirects as e:
332
+ return {"error": f"Fetch URL error: {e!s}", "url": url, "category": "redirects"}
333
+ except requests.exceptions.RequestException as e:
334
+ return {"error": f"Fetch URL error: {e!s}", "url": url, "category": "network"}
335
+
336
+ markdown_content = markdownify(response.text)
337
+ return {
338
+ "url": str(response.url),
339
+ "markdown_content": markdown_content,
340
+ "status_code": response.status_code,
341
+ "content_length": len(markdown_content),
342
+ }
343
+
344
+
345
+ def _fetch_with_redirects(url: str, *, timeout: int) -> Any: # noqa: ANN401 # requests.Response, but kept dynamic to avoid eager import
346
+ """Fetch `url`, re-validating each redirect hop against the SSRF guard.
347
+
348
+ Each hop is validated by `_validate_url` and its connection pinned to
349
+ the validated IP via `_pinned_dns`. Caps at `_MAX_FETCH_REDIRECTS`
350
+ redirects (so up to `_MAX_FETCH_REDIRECTS + 1` total hops counting the
351
+ initial request). Network/HTTP errors propagate as
352
+ `requests.exceptions.RequestException` (or its subclasses).
353
+
354
+ Args:
355
+ url: Initial URL to fetch.
356
+ timeout: Per-request timeout in seconds.
357
+
358
+ Returns:
359
+ The final `requests.Response` for the non-redirect terminal hop.
360
+
361
+ Raises:
362
+ _UrlValidationError: If any hop fails SSRF validation or returns a
363
+ 3xx without a `Location` header.
364
+ requests.exceptions.TooManyRedirects: If the redirect cap is exceeded.
365
+ """
366
+ import requests
367
+
368
+ current_url = url
369
+ session = requests.Session()
370
+ # DNS pinning only protects the direct target connection. Environment
371
+ # proxies resolve the target separately, so they must be disabled here.
372
+ session.trust_env = False
373
+ for _hop in range(_MAX_FETCH_REDIRECTS + 1):
374
+ validated_ips = _validate_url(current_url)
375
+ hostname = urlparse(current_url).hostname
376
+ # `_validate_url` raises if hostname is missing, so this is non-None.
377
+ assert hostname is not None # noqa: S101 # invariant from _validate_url
378
+ encoded_hostname = hostname.encode("idna").decode("ascii")
379
+
380
+ with _pinned_dns(encoded_hostname, validated_ips):
381
+ response = session.get(
382
+ current_url,
383
+ timeout=timeout,
384
+ headers={"User-Agent": "Mozilla/5.0 (compatible; DeepAgents/1.0)"},
385
+ allow_redirects=False,
386
+ )
387
+
388
+ # 300-399 covers every redirect class. `requests.Response.is_redirect`
389
+ # also checks for a `Location` header, which would hide malformed 3xx
390
+ # responses — so we check the raw status code instead.
391
+ if 300 <= response.status_code < 400: # noqa: PLR2004 # HTTP redirect class
392
+ location = response.headers.get("Location")
393
+ if not location:
394
+ msg = (
395
+ f"Redirect response (status {response.status_code}) at "
396
+ f"{current_url!r} is missing a Location header"
397
+ )
398
+ raise _UrlValidationError(msg)
399
+ current_url = urljoin(current_url, location)
400
+ continue
401
+
402
+ response.raise_for_status()
403
+ return response
404
+
405
+ msg = f"Exceeded {_MAX_FETCH_REDIRECTS} redirects starting from {url!r}"
406
+ raise requests.exceptions.TooManyRedirects(msg)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "deepagents-code"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "Terminal interface for Deep Agents - interactive AI agent with file operations, shell access, and sub-agent capabilities."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -45,7 +45,7 @@ cleanup() {
45
45
  if [ $exit_code -ne 0 ]; then
46
46
  echo "" >&2
47
47
  log_error "Installation failed (exit code ${exit_code}). See errors above."
48
- log_error "For help, visit: https://docs.langchain.com/oss/python/deepagents/cli/overview"
48
+ log_error "For help, visit: https://docs.langchain.com/oss/python/deepagents/code/overview"
49
49
  fi
50
50
  }
51
51
  trap cleanup EXIT
@@ -436,4 +436,4 @@ echo ""
436
436
  printf "${GREEN}✔${NC} Setup complete. Run: ${BOLD}deepagents${NC}\n"
437
437
  echo ""
438
438
  echo "For help and support, see the Deep Agents Code docs:"
439
- echo " https://docs.langchain.com/oss/python/deepagents/cli/overview"
439
+ echo " https://docs.langchain.com/oss/python/deepagents/code/overview"
@@ -1034,7 +1034,7 @@ class TestCheckMcpProjectTrustPrompt:
1034
1034
  captured = capsys.readouterr()
1035
1035
  flattened = captured.err.replace("\n", "")
1036
1036
  assert (
1037
- "https://docs.langchain.com/oss/python/deepagents/cli/"
1037
+ "https://docs.langchain.com/oss/python/deepagents/code/"
1038
1038
  "mcp-tools#project-level-trust" in flattened
1039
1039
  )
1040
1040
  assert "Learn more:" in captured.err
@@ -79,7 +79,7 @@ class TestErrorMessageMarkupSafety:
79
79
  """Pre-built `Content` with `link` spans passes through to render output."""
80
80
  from textual.style import Style as TStyle
81
81
 
82
- url = "https://docs.langchain.com/oss/python/deepagents/cli/providers"
82
+ url = "https://docs.langchain.com/oss/python/deepagents/code/providers"
83
83
  body = Content.assemble(
84
84
  "see ",
85
85
  (url, TStyle(underline=True, link=url)),
@@ -241,7 +241,7 @@ def test_agentcore_delete_untracked_session() -> None:
241
241
  [
242
242
  ("agentcore", "/tmp"),
243
243
  ("daytona", "/home/daytona"),
244
- ("langsmith", "/tmp"),
244
+ ("langsmith", "/root"),
245
245
  ("modal", "/workspace"),
246
246
  ("runloop", "/home/user"),
247
247
  ],