deepagents-code 0.1.7__tar.gz → 0.1.8__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 (254) hide show
  1. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/CHANGELOG.md +23 -0
  2. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/PKG-INFO +2 -2
  3. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_version.py +1 -1
  4. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/agent.py +5 -2
  5. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/app.py +183 -51
  6. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/ask_user.py +5 -0
  7. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/command_registry.py +17 -0
  8. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/extras_info.py +70 -0
  9. deepagents_code-0.1.8/deepagents_code/filesystem_empty_result.py +88 -0
  10. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/local_context.py +3 -12
  11. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/main.py +7 -7
  12. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_auth.py +250 -12
  13. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_tools.py +11 -0
  14. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/unicode_security.py +47 -0
  15. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/update_check.py +62 -13
  16. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/mcp_login.py +123 -30
  17. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/mcp_viewer.py +166 -37
  18. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/model_selector.py +16 -3
  19. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/welcome.py +2 -1
  20. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/pyproject.toml +2 -2
  21. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/scripts/install.sh +22 -7
  22. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_app.py +175 -14
  23. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_command_registry.py +12 -0
  24. deepagents_code-0.1.8/tests/unit_tests/test_cursor_blink.py +59 -0
  25. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_extras_info.py +34 -0
  26. deepagents_code-0.1.8/tests/unit_tests/test_filesystem_empty_result.py +160 -0
  27. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_install_command.py +12 -4
  28. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_main_args.py +57 -2
  29. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_auth.py +405 -1
  30. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_login_modal.py +103 -1
  31. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_viewer.py +181 -43
  32. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_model_selector.py +35 -0
  33. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_skill_invocation.py +3 -0
  34. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_unicode_security.py +69 -0
  35. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_update_check.py +129 -3
  36. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_welcome.py +2 -2
  37. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/uv.lock +17 -14
  38. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/.gitignore +0 -0
  39. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/AGENTS.md +0 -0
  40. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/COMMANDS.md +0 -0
  41. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/DEV.md +0 -0
  42. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/Makefile +0 -0
  43. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/README.md +0 -0
  44. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/THREAT_MODEL.md +0 -0
  45. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/__init__.py +0 -0
  46. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/__main__.py +0 -0
  47. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_ask_user_types.py +0 -0
  48. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_cli_context.py +0 -0
  49. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_constants.py +0 -0
  50. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_debug.py +0 -0
  51. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_env_vars.py +0 -0
  52. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_git.py +0 -0
  53. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_server_config.py +0 -0
  54. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_session_stats.py +0 -0
  55. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_startup_error.py +0 -0
  56. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_testing_models.py +0 -0
  57. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/_textual_patches.py +0 -0
  58. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/app.tcss +0 -0
  59. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/auth_store.py +0 -0
  60. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/built_in_skills/__init__.py +0 -0
  61. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
  62. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
  63. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  64. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  65. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/clipboard.py +0 -0
  66. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/config.py +0 -0
  67. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/configurable_model.py +0 -0
  68. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/default_agent_prompt.md +0 -0
  69. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/editor.py +0 -0
  70. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/event_bus.py +0 -0
  71. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/file_ops.py +0 -0
  72. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/formatting.py +0 -0
  73. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/hooks.py +0 -0
  74. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/input.py +0 -0
  75. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/integrations/__init__.py +0 -0
  76. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/integrations/sandbox_factory.py +0 -0
  77. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/integrations/sandbox_provider.py +0 -0
  78. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/iterm_cursor_guide.py +0 -0
  79. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_commands.py +0 -0
  80. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_disabled.py +0 -0
  81. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_login_service.py +0 -0
  82. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_oauth_ui.py +0 -0
  83. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_providers/__init__.py +0 -0
  84. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_providers/_registry.py +0 -0
  85. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_providers/base.py +0 -0
  86. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_providers/github.py +0 -0
  87. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_providers/slack.py +0 -0
  88. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/mcp_trust.py +0 -0
  89. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/media_utils.py +0 -0
  90. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/model_config.py +0 -0
  91. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/non_interactive.py +0 -0
  92. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/notifications.py +0 -0
  93. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/offload.py +0 -0
  94. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/onboarding.py +0 -0
  95. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/output.py +0 -0
  96. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/project_utils.py +0 -0
  97. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/py.typed +0 -0
  98. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/remote_client.py +0 -0
  99. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/resume_state.py +0 -0
  100. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/server.py +0 -0
  101. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/server_graph.py +0 -0
  102. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/server_manager.py +0 -0
  103. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/sessions.py +0 -0
  104. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/skills/__init__.py +0 -0
  105. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/skills/commands.py +0 -0
  106. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/skills/invocation.py +0 -0
  107. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/skills/load.py +0 -0
  108. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/state_migration.py +0 -0
  109. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/subagents.py +0 -0
  110. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/system_prompt.md +0 -0
  111. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/terminal_capabilities.py +0 -0
  112. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/terminal_escape.py +0 -0
  113. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/textual_adapter.py +0 -0
  114. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/theme.py +0 -0
  115. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/tool_display.py +0 -0
  116. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/tools.py +0 -0
  117. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/ui.py +0 -0
  118. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/__init__.py +0 -0
  119. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/_links.py +0 -0
  120. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/agent_selector.py +0 -0
  121. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/approval.py +0 -0
  122. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/ask_user.py +0 -0
  123. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/auth.py +0 -0
  124. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/autocomplete.py +0 -0
  125. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/chat_input.py +0 -0
  126. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/diff.py +0 -0
  127. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/history.py +0 -0
  128. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/launch_init.py +0 -0
  129. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/loading.py +0 -0
  130. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/mcp_reconnect.py +0 -0
  131. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/message_store.py +0 -0
  132. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/messages.py +0 -0
  133. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/notification_center.py +0 -0
  134. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/notification_detail.py +0 -0
  135. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/notification_settings.py +0 -0
  136. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/status.py +0 -0
  137. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/theme_selector.py +0 -0
  138. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/thread_selector.py +0 -0
  139. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/tool_renderers.py +0 -0
  140. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/tool_widgets.py +0 -0
  141. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/update_available.py +0 -0
  142. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/deepagents_code/widgets/update_progress.py +0 -0
  143. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/examples/skills/arxiv-search/SKILL.md +0 -0
  144. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  145. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/examples/skills/langgraph-docs/SKILL.md +0 -0
  146. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/examples/skills/skill-creator/SKILL.md +0 -0
  147. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  148. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  149. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/examples/skills/web-research/SKILL.md +0 -0
  150. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/images/tui.png +0 -0
  151. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/scripts/check_imports.py +0 -0
  152. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/scripts/debug_server.sh +0 -0
  153. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/scripts/generate_commands_catalog.py +0 -0
  154. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/README.md +0 -0
  155. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/__init__.py +0 -0
  156. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/benchmarks/__init__.py +0 -0
  157. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  158. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  159. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/conftest.py +0 -0
  160. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/test_acp_mode.py +0 -0
  161. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/test_compact_resume.py +0 -0
  162. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/test_sandbox_factory.py +0 -0
  163. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/integration_tests/test_sandbox_operations.py +0 -0
  164. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/__init__.py +0 -0
  165. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/conftest.py +0 -0
  166. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/skills/__init__.py +0 -0
  167. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/skills/test_commands.py +0 -0
  168. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/skills/test_load.py +0 -0
  169. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/skills/test_skills_json.py +0 -0
  170. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_agent.py +0 -0
  171. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_agent_friendly.py +0 -0
  172. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_agent_selector.py +0 -0
  173. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_approval.py +0 -0
  174. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_args.py +0 -0
  175. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_ask_user.py +0 -0
  176. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  177. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_auth_store.py +0 -0
  178. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_auth_widgets.py +0 -0
  179. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_autocomplete.py +0 -0
  180. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_charset.py +0 -0
  181. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_chat_input.py +0 -0
  182. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_clipboard.py +0 -0
  183. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_compact_tool.py +0 -0
  184. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_config.py +0 -0
  185. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_configurable_model.py +0 -0
  186. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_debug.py +0 -0
  187. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_editor.py +0 -0
  188. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_end_to_end.py +0 -0
  189. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_env_vars.py +0 -0
  190. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_event_bus.py +0 -0
  191. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_exception_handling.py +0 -0
  192. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_file_ops.py +0 -0
  193. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_formatting.py +0 -0
  194. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_git.py +0 -0
  195. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_history.py +0 -0
  196. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_hooks.py +0 -0
  197. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_imports.py +0 -0
  198. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_input_parsing.py +0 -0
  199. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
  200. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_launch_init.py +0 -0
  201. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_links.py +0 -0
  202. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_loading.py +0 -0
  203. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_local_context.py +0 -0
  204. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_main.py +0 -0
  205. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_main_acp_mode.py +0 -0
  206. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_commands.py +0 -0
  207. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_disabled.py +0 -0
  208. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_login_service.py +0 -0
  209. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
  210. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_reconnect.py +0 -0
  211. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_tools.py +0 -0
  212. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_mcp_trust.py +0 -0
  213. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_media_utils.py +0 -0
  214. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_message_store.py +0 -0
  215. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_messages.py +0 -0
  216. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_model_config.py +0 -0
  217. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_model_switch.py +0 -0
  218. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_non_interactive.py +0 -0
  219. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_notification_center.py +0 -0
  220. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_notification_detail.py +0 -0
  221. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_notifications.py +0 -0
  222. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_offload.py +0 -0
  223. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  224. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_onboarding.py +0 -0
  225. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_output.py +0 -0
  226. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_reload.py +0 -0
  227. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_remote_client.py +0 -0
  228. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_resume_state.py +0 -0
  229. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_sandbox_factory.py +0 -0
  230. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_server.py +0 -0
  231. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_server_config.py +0 -0
  232. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_server_graph.py +0 -0
  233. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_server_helpers.py +0 -0
  234. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_server_manager.py +0 -0
  235. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_session_stats.py +0 -0
  236. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_sessions.py +0 -0
  237. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_shell_allow_list.py +0 -0
  238. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_startup_fast_paths.py +0 -0
  239. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_state_migration.py +0 -0
  240. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_status.py +0 -0
  241. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_subagents.py +0 -0
  242. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  243. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_terminal_escape.py +0 -0
  244. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_textual_adapter.py +0 -0
  245. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_textual_patches.py +0 -0
  246. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_theme.py +0 -0
  247. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_thread_selector.py +0 -0
  248. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_tool_display.py +0 -0
  249. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_ui.py +0 -0
  250. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_update_available.py +0 -0
  251. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_update_progress.py +0 -0
  252. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/test_version.py +0 -0
  253. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/tools/__init__.py +0 -0
  254. {deepagents_code-0.1.7 → deepagents_code-0.1.8}/tests/unit_tests/tools/test_fetch_url.py +0 -0
@@ -2,6 +2,27 @@
2
2
 
3
3
  # Deep Agents Code Changelog
4
4
 
5
+ ## [0.1.8](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.7...deepagents-code==0.1.8) (2026-06-02)
6
+
7
+ ### Features
8
+
9
+ * List valid extras when `/install` has no argument ([#3695](https://github.com/langchain-ai/deepagents/issues/3695)) ([c7d529c](https://github.com/langchain-ai/deepagents/commit/c7d529ca0fc478dec9060ea04bcc8589f9b1cd3a))
10
+ * Add MCP error detail modal ([#3687](https://github.com/langchain-ai/deepagents/issues/3687)) ([4ae4754](https://github.com/langchain-ai/deepagents/commit/4ae475453ce0df6d6b057b7e163396aa27d55143))
11
+
12
+ ### Bug Fixes
13
+
14
+ * Allow recovery commands when startup fails ([#3706](https://github.com/langchain-ai/deepagents/issues/3706)) ([727d022](https://github.com/langchain-ai/deepagents/commit/727d022cd1526836c3d1de997c1f036e870881f7))
15
+ * Preserve extras during install ([#3707](https://github.com/langchain-ai/deepagents/issues/3707)) ([e636ce9](https://github.com/langchain-ai/deepagents/commit/e636ce9e979fd1c30335ec340acdabbd0a5ae79e))
16
+ * Normalize empty file list tool output ([#3697](https://github.com/langchain-ai/deepagents/issues/3697)) ([b67aead](https://github.com/langchain-ai/deepagents/commit/b67aead2b86e04aaee8f2dbfba7b263e3e23597d))
17
+ * Point MCP re-enable guidance at `Ctrl+R` ([#3688](https://github.com/langchain-ai/deepagents/issues/3688)) ([15ca302](https://github.com/langchain-ai/deepagents/commit/15ca3029f18fa38c1592859febc2a6d0469bff2d))
18
+ * Preserve MCP token refresh when metadata discovery fails ([#3685](https://github.com/langchain-ai/deepagents/issues/3685)) ([afafeeb](https://github.com/langchain-ai/deepagents/commit/afafeeb471c4008d4eb4263ec478cf868833fe0b))
19
+ * Reduce OAuth login modal noise ([#3693](https://github.com/langchain-ai/deepagents/issues/3693)) ([0e8a780](https://github.com/langchain-ai/deepagents/commit/0e8a780e2dfea2e22ac44545a16279dbe30eb8ee))
20
+ * Repair MCP OAuth login redirect and stale client registration ([#3692](https://github.com/langchain-ai/deepagents/issues/3692)) ([f741293](https://github.com/langchain-ai/deepagents/commit/f741293524f7d47eb8a16a3cd4def336c3c3c13f))
21
+ * Search all models from `/model` filter ([#3690](https://github.com/langchain-ai/deepagents/issues/3690)) ([5fcb877](https://github.com/langchain-ai/deepagents/commit/5fcb877d094c4504f671bb7aeb52efa7bf3a5b48))
22
+ * Serialize `QueuedUserMessage` as user input ([#3708](https://github.com/langchain-ai/deepagents/issues/3708)) ([307d598](https://github.com/langchain-ai/deepagents/commit/307d59826da9b1ddcbcdab8dccef6d18ecf16d10))
23
+ * Serialize cold SDK imports ([#3712](https://github.com/langchain-ai/deepagents/issues/3712)) ([fb2adc0](https://github.com/langchain-ai/deepagents/commit/fb2adc0585e978b12646602ba922e252abf41f81))
24
+ * Pluralize singular MCP login splash text ([#3689](https://github.com/langchain-ai/deepagents/issues/3689)) ([492b0fc](https://github.com/langchain-ai/deepagents/commit/492b0fc9209e13cd7004a255ef67b31b7e78e95e))
25
+
5
26
  ## [0.1.7](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.6...deepagents-code==0.1.7) (2026-05-30)
6
27
 
7
28
  ### Features
@@ -102,5 +123,7 @@ Hello world! Ported from `libs/cli`.
102
123
 
103
124
  ---
104
125
 
126
+ ## Prior Releases
127
+
105
128
  `deepagents-code` was forked from `deepagents-cli` at v0.1.0 (2026-05-12).
106
129
  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.7
3
+ Version: 0.1.8
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/
@@ -33,7 +33,7 @@ Requires-Dist: langchain-anthropic<2.0.0,>=1.4.3
33
33
  Requires-Dist: langchain-google-genai<5.0.0,>=4.2.2
34
34
  Requires-Dist: langchain-mcp-adapters<1.0.0,>=0.2.2
35
35
  Requires-Dist: langchain-openai<2.0.0,>=1.2.1
36
- Requires-Dist: langchain<2.0.0,>=1.3.2
36
+ Requires-Dist: langchain<2.0.0,>=1.3.4
37
37
  Requires-Dist: langgraph-checkpoint-sqlite<4.0.0,>=3.1.0
38
38
  Requires-Dist: langgraph-cli[inmem]<1.0.0,>=0.4.24
39
39
  Requires-Dist: langgraph-runtime-inmem<1.0.0,>=0.28.1
@@ -2,7 +2,7 @@
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.7" # x-release-please-version
5
+ __version__ = "0.1.8" # x-release-please-version
6
6
 
7
7
  DOCS_URL = "https://docs.langchain.com/oss/python/deepagents/code"
8
8
  """URL for `deepagents-code` documentation."""
@@ -62,6 +62,7 @@ from deepagents_code.config import (
62
62
  settings,
63
63
  )
64
64
  from deepagents_code.configurable_model import ConfigurableModelMiddleware
65
+ from deepagents_code.filesystem_empty_result import _FilesystemEmptyResultMiddleware
65
66
  from deepagents_code.integrations.sandbox_factory import get_default_working_dir
66
67
  from deepagents_code.local_context import (
67
68
  LocalContextMiddleware,
@@ -1240,8 +1241,10 @@ def create_cli_agent(
1240
1241
  custom_subagents.append(general_purpose_subagent)
1241
1242
 
1242
1243
  # Build middleware stack based on enabled features
1243
- agent_middleware = []
1244
- agent_middleware.append(ConfigurableModelMiddleware())
1244
+ agent_middleware = [
1245
+ ConfigurableModelMiddleware(),
1246
+ _FilesystemEmptyResultMiddleware(),
1247
+ ]
1245
1248
 
1246
1249
  # Resume state: declares the `_context_tokens` and `_model_spec` channels
1247
1250
  # and writes them from `after_model` (token count from the latest
@@ -103,6 +103,14 @@ _DEFERRED_START_NOTICE = (
103
103
  # read the same pre-mutation state and then clobber the other's keys.
104
104
  _CONFIG_WRITE_LOCK = threading.Lock()
105
105
 
106
+ _DEEPAGENTS_IMPORT_LOCK = threading.RLock()
107
+ """Serializes process-local cold imports into the Deep Agents SDK graph.
108
+
109
+ The SDK currently has a package-to-backend circular import that is safe when a
110
+ single thread imports it re-entrantly, but can trip CPython's per-module import
111
+ deadlock detector when two threads cold-import overlapping modules.
112
+ """
113
+
106
114
  _MESSAGE_TIMESTAMP_FOOTER_CLASS = "message-timestamp-footer"
107
115
 
108
116
  _TIMESTAMP_FOOTER_EXCLUDED_TYPES: frozenset[MessageType] = frozenset(
@@ -127,6 +135,32 @@ def _is_message_timestamp_footer(widget: Widget) -> bool:
127
135
  return widget.has_class(_MESSAGE_TIMESTAMP_FOOTER_CLASS)
128
136
 
129
137
 
138
+ def _create_model_with_deepagents_import_lock(
139
+ model_spec: str | None = None,
140
+ *,
141
+ extra_kwargs: dict[str, Any] | None = None,
142
+ profile_overrides: dict[str, Any] | None = None,
143
+ ) -> ModelResult:
144
+ """Create a model while serializing Deep Agents SDK import entry.
145
+
146
+ Args:
147
+ model_spec: Model specification in `provider:model` format.
148
+ extra_kwargs: Extra model constructor kwargs.
149
+ profile_overrides: Model profile metadata overrides.
150
+
151
+ Returns:
152
+ Created model and resolved metadata.
153
+ """
154
+ with _DEEPAGENTS_IMPORT_LOCK:
155
+ from deepagents_code.config import create_model
156
+
157
+ return create_model(
158
+ model_spec,
159
+ extra_kwargs=extra_kwargs,
160
+ profile_overrides=profile_overrides,
161
+ )
162
+
163
+
130
164
  @dataclass(frozen=True)
131
165
  class _ConfigWriteResult:
132
166
  """Result of a config write with TUI-facing failure context."""
@@ -156,6 +190,7 @@ if TYPE_CHECKING:
156
190
  from textual.worker import Worker
157
191
 
158
192
  from deepagents_code._ask_user_types import AskUserWidgetResult, Question
193
+ from deepagents_code.config import ModelResult
159
194
  from deepagents_code.event_bus import EventSource, ExternalEvent
160
195
  from deepagents_code.mcp_tools import MCPServerInfo
161
196
  from deepagents_code.model_config import MissingProviderPackageError
@@ -525,6 +560,49 @@ def save_theme_preference(name: str) -> bool:
525
560
  return _save_theme_preference_result(name).ok
526
561
 
527
562
 
563
+ def _load_cursor_blink_preference() -> bool:
564
+ """Load the saved cursor-blink preference from `~/.deepagents/config.toml`.
565
+
566
+ The chat input cursor blink can be turned off by setting
567
+ `[ui].cursor_blink = false` in the config file. There is no in-app command
568
+ for this; the file is edited manually.
569
+
570
+ Returns:
571
+ The saved `[ui].cursor_blink` value, or `True` (blink on) when unset,
572
+ unreadable, or malformed.
573
+ """
574
+ import tomllib
575
+
576
+ from deepagents_code.model_config import DEFAULT_CONFIG_PATH
577
+
578
+ if not DEFAULT_CONFIG_PATH.exists():
579
+ return True
580
+ try:
581
+ with DEFAULT_CONFIG_PATH.open("rb") as f:
582
+ data = tomllib.load(f)
583
+ except (tomllib.TOMLDecodeError, PermissionError, OSError) as exc:
584
+ logger.warning("Could not read config for cursor blink preference: %s", exc)
585
+ return True
586
+
587
+ ui = data.get("ui", {})
588
+ if not isinstance(ui, dict):
589
+ logger.warning(
590
+ "[ui] should be a table; got %s while loading cursor blink preference",
591
+ type(ui).__name__,
592
+ )
593
+ return True
594
+
595
+ value = ui.get("cursor_blink")
596
+ if isinstance(value, bool):
597
+ return value
598
+ if value is not None:
599
+ logger.warning(
600
+ "[ui].cursor_blink should be a boolean; got %s",
601
+ type(value).__name__,
602
+ )
603
+ return True
604
+
605
+
528
606
  def _save_terminal_theme_mapping_result(
529
607
  term_program: str,
530
608
  name: str,
@@ -1328,6 +1406,10 @@ class DeepAgentsApp(App):
1328
1406
  Loaded from the user's saved preference (or the default) so the app
1329
1407
  boots with consistent colors before `/theme` runs.
1330
1408
  """
1409
+
1410
+ self._cursor_blink_enabled = _load_cursor_blink_preference()
1411
+ """Whether the chat input cursor should blink (user preference)."""
1412
+
1331
1413
  self.sync_terminal_background()
1332
1414
 
1333
1415
  # Injected session config
@@ -1612,14 +1694,6 @@ class DeepAgentsApp(App):
1612
1694
  loop re-enters the same module graph (see that method for why).
1613
1695
  """
1614
1696
 
1615
- self._deepagents_import_lock = asyncio.Lock()
1616
- """Serializes cold imports into the Deep Agents graph after prewarm.
1617
-
1618
- Startup model creation and skill discovery can both be triggered during
1619
- first paint. If prewarm failed or did not cover a transitive module,
1620
- both paths may cold-import overlapping modules at the same time.
1621
- """
1622
-
1623
1697
  # Lifecycle flags & re-entry guards
1624
1698
  self._connecting = (
1625
1699
  server_kwargs is not None and not self._server_startup_deferred
@@ -1963,6 +2037,7 @@ class DeepAgentsApp(App):
1963
2037
 
1964
2038
  self._status_bar = self.query_one("#status-bar", StatusBar)
1965
2039
  self._chat_input = self.query_one("#input-area", ChatInput)
2040
+ self._chat_input.set_cursor_blink(blink=self._cursor_blink_enabled)
1966
2041
 
1967
2042
  # Apply any skill commands discovered before the widget was mounted
1968
2043
  if self._discovered_skills:
@@ -2406,8 +2481,9 @@ class DeepAgentsApp(App):
2406
2481
  # graph in separate workers. Let prewarm finish first so CPython's
2407
2482
  # per-module import locks cannot form a cycle.
2408
2483
  await self._await_prewarm_imports()
2409
- async with self._deepagents_import_lock:
2410
- skills, roots = await asyncio.to_thread(self._discover_skills_and_roots)
2484
+ skills, roots = await asyncio.to_thread(
2485
+ self._discover_skills_and_roots_with_import_lock,
2486
+ )
2411
2487
  except OSError:
2412
2488
  logger.warning(
2413
2489
  "Filesystem error during skill discovery",
@@ -2465,6 +2541,17 @@ class DeepAgentsApp(App):
2465
2541
  assistant_id = self._assistant_id or DEFAULT_ASSISTANT_ID
2466
2542
  return discover_skills_and_roots(assistant_id)
2467
2543
 
2544
+ def _discover_skills_and_roots_with_import_lock(
2545
+ self,
2546
+ ) -> tuple[list[ExtendedSkillMetadata], list[Path]]:
2547
+ """Discover skills while serializing Deep Agents SDK import entry.
2548
+
2549
+ Returns:
2550
+ Tuple of `(skill metadata list, pre-resolved containment roots)`.
2551
+ """
2552
+ with _DEEPAGENTS_IMPORT_LOCK:
2553
+ return self._discover_skills_and_roots()
2554
+
2468
2555
  async def _resolve_resume_thread(self) -> None:
2469
2556
  """Resolve a `-r` resume intent into a concrete thread ID.
2470
2557
 
@@ -2595,7 +2682,6 @@ class DeepAgentsApp(App):
2595
2682
  # `_await_prewarm_imports` for the deadlock rationale.
2596
2683
  await self._await_prewarm_imports()
2597
2684
 
2598
- from deepagents_code.config import create_model
2599
2685
  from deepagents_code.model_config import (
2600
2686
  ModelConfigError,
2601
2687
  save_recent_model,
@@ -2603,8 +2689,10 @@ class DeepAgentsApp(App):
2603
2689
  )
2604
2690
 
2605
2691
  try:
2606
- async with self._deepagents_import_lock:
2607
- result = create_model(**self._model_kwargs)
2692
+ result = await asyncio.to_thread(
2693
+ _create_model_with_deepagents_import_lock,
2694
+ **self._model_kwargs,
2695
+ )
2608
2696
  except ModelConfigError as exc:
2609
2697
  self.post_message(self.ServerStartFailed(error=exc))
2610
2698
  return
@@ -2958,12 +3046,13 @@ class DeepAgentsApp(App):
2958
3046
  # tool_display — hit on first message send and first tool
2959
3047
  # approval. Best-effort: missing optional deps should not block the
2960
3048
  # TUI from rendering.
2961
- from deepagents.backends import DEFAULT_EXECUTE_TIMEOUT # noqa: F401
2962
- from langchain.agents.middleware.human_in_the_loop import ( # noqa: F401
2963
- ApproveDecision,
2964
- )
2965
- from langchain_core.messages import AIMessage # noqa: F401
2966
- from langgraph.types import Command # noqa: F401
3049
+ with _DEEPAGENTS_IMPORT_LOCK:
3050
+ from deepagents.backends import DEFAULT_EXECUTE_TIMEOUT # noqa: F401
3051
+ from langchain.agents.middleware.human_in_the_loop import ( # noqa: F401
3052
+ ApproveDecision,
3053
+ )
3054
+ from langchain_core.messages import AIMessage # noqa: F401
3055
+ from langgraph.types import Command # noqa: F401
2967
3056
  except Exception:
2968
3057
  logger.warning("Could not prewarm third-party imports", exc_info=True)
2969
3058
 
@@ -3349,9 +3438,13 @@ class DeepAgentsApp(App):
3349
3438
  force = "--force" in parts[1:]
3350
3439
  extras = [p for p in parts[1:] if not p.startswith("-")]
3351
3440
  if not extras:
3441
+ from deepagents_code.extras_info import format_known_extras
3442
+
3352
3443
  await self._mount_message(
3353
3444
  AppMessage(
3354
- "Usage: /install <extra> [--force]\nExample: /install quickjs",
3445
+ "Usage: /install <extra> [--force]\n"
3446
+ "Example: /install quickjs\n\n"
3447
+ f"{format_known_extras()}",
3355
3448
  ),
3356
3449
  )
3357
3450
  return
@@ -3372,6 +3465,7 @@ class DeepAgentsApp(App):
3372
3465
  KNOWN_EXTRAS,
3373
3466
  MODEL_PROVIDER_EXTRAS,
3374
3467
  SANDBOX_EXTRAS,
3468
+ ExtrasIntrospectionError,
3375
3469
  )
3376
3470
  from deepagents_code.update_check import (
3377
3471
  create_update_log_path,
@@ -3406,12 +3500,20 @@ class DeepAgentsApp(App):
3406
3500
  return
3407
3501
 
3408
3502
  if extra not in KNOWN_EXTRAS and not force:
3503
+ try:
3504
+ manual_cmd = await asyncio.to_thread(install_extra_command, extra)
3505
+ except (ExtrasIntrospectionError, ValueError) as exc:
3506
+ logger.warning("/install command failed", exc_info=True)
3507
+ await self._mount_message(
3508
+ ErrorMessage(f"Install failed: {type(exc).__name__}: {exc}"),
3509
+ )
3510
+ return
3409
3511
  known = ", ".join(sorted(KNOWN_EXTRAS))
3410
3512
  await self._mount_message(
3411
3513
  AppMessage(
3412
3514
  f"'{extra}' is not a known extra.\n"
3413
3515
  f"Known extras: {known}\n\n"
3414
- f"This would run: `{install_extra_command(extra)}`\n"
3516
+ f"This would run: `{manual_cmd}`\n"
3415
3517
  f"Re-run with `--force` to install anyway: "
3416
3518
  f"`/install {extra} --force`",
3417
3519
  ),
@@ -3422,7 +3524,16 @@ class DeepAgentsApp(App):
3422
3524
  await self._mount_message(
3423
3525
  AppMessage(f"Installing extra '{extra}'..."),
3424
3526
  )
3425
- manual_cmd = install_extra_command(extra)
3527
+ try:
3528
+ manual_cmd = await asyncio.to_thread(install_extra_command, extra)
3529
+ except (ExtrasIntrospectionError, ValueError) as exc:
3530
+ logger.warning("/install command failed", exc_info=True)
3531
+ await self._mount_message(
3532
+ ErrorMessage(
3533
+ f"Install failed: {type(exc).__name__}: {exc}\nLog: {log_path}",
3534
+ ),
3535
+ )
3536
+ return
3426
3537
  try:
3427
3538
  success, output = await perform_install_extra(extra, log_path=log_path)
3428
3539
  except (OSError, asyncio.CancelledError) as exc:
@@ -4843,9 +4954,21 @@ class DeepAgentsApp(App):
4843
4954
  BYPASS_WHEN_CONNECTING,
4844
4955
  IMMEDIATE_UI,
4845
4956
  SIDE_EFFECT_FREE,
4957
+ STARTUP_RECOVERY_COMMANDS,
4846
4958
  )
4847
4959
 
4848
4960
  cmd = value.split(maxsplit=1)[0] if value else ""
4961
+ # Recovery escape hatch: when startup failed (`_server_startup_error`
4962
+ # set) and nothing is running, the commands that repair the session
4963
+ # must run instead of being parked behind the failure they fix — e.g.
4964
+ # `/install <pkg>` for a missing provider package. Gated on no active
4965
+ # work so a reinstall never swaps the running binary mid-turn.
4966
+ if (
4967
+ cmd in STARTUP_RECOVERY_COMMANDS
4968
+ and self._server_startup_error is not None
4969
+ and not (self._agent_running or self._shell_running)
4970
+ ):
4971
+ return True
4849
4972
  if cmd in BYPASS_WHEN_CONNECTING:
4850
4973
  return self._connecting and not (self._agent_running or self._shell_running)
4851
4974
  if cmd in IMMEDIATE_UI:
@@ -5841,7 +5964,7 @@ class DeepAgentsApp(App):
5841
5964
  if cached is None:
5842
5965
  try:
5843
5966
  skills, allowed_roots = await asyncio.to_thread(
5844
- self._discover_skills_and_roots,
5967
+ self._discover_skills_and_roots_with_import_lock,
5845
5968
  )
5846
5969
  # Backfill cache so subsequent invocations are fast
5847
5970
  self._discovered_skills = skills
@@ -6010,31 +6133,33 @@ class DeepAgentsApp(App):
6010
6133
  """Resolve the offload retention budget as a human-readable string.
6011
6134
 
6012
6135
  Instantiates a model and computes summarization defaults, so this is
6013
- not a trivial accessor.
6136
+ not a trivial accessor. Call only from a worker thread; cold imports
6137
+ and model construction run under the process-wide import lock.
6014
6138
 
6015
6139
  Returns:
6016
6140
  A string like `"20.0K (10% of 200.0K)"` or
6017
6141
  `"last 6 messages"`, or `None` if the budget cannot be determined.
6018
6142
  """
6019
- from deepagents_code.config import create_model, settings
6143
+ from deepagents_code.config import settings
6020
6144
 
6021
6145
  try:
6022
- from deepagents.middleware.summarization import (
6023
- compute_summarization_defaults,
6024
- )
6146
+ with _DEEPAGENTS_IMPORT_LOCK:
6147
+ from deepagents.middleware.summarization import (
6148
+ compute_summarization_defaults,
6149
+ )
6025
6150
 
6026
- model_spec = f"{settings.model_provider}:{settings.model_name}"
6027
- result = create_model(
6028
- model_spec,
6029
- profile_overrides=self._profile_override,
6030
- )
6031
- defaults = compute_summarization_defaults(result.model)
6032
- from deepagents_code.offload import format_offload_limit
6151
+ model_spec = f"{settings.model_provider}:{settings.model_name}"
6152
+ result = _create_model_with_deepagents_import_lock(
6153
+ model_spec,
6154
+ profile_overrides=self._profile_override,
6155
+ )
6156
+ defaults = compute_summarization_defaults(result.model)
6157
+ from deepagents_code.offload import format_offload_limit
6033
6158
 
6034
- return format_offload_limit(
6035
- defaults["keep"],
6036
- settings.model_context_limit,
6037
- )
6159
+ return format_offload_limit(
6160
+ defaults["keep"],
6161
+ settings.model_context_limit,
6162
+ )
6038
6163
  except Exception: # best-effort for /tokens display
6039
6164
  logger.debug("Failed to compute offload budget string", exc_info=True)
6040
6165
  return None
@@ -6918,6 +7043,17 @@ class DeepAgentsApp(App):
6918
7043
  if not messages.is_attached:
6919
7044
  return
6920
7045
 
7046
+ if isinstance(widget, QueuedUserMessage):
7047
+ # Queued placeholders mount at the bottom and stay out of the
7048
+ # message store; drain remounts them as real UserMessage widgets.
7049
+ await messages.mount(widget)
7050
+ try:
7051
+ input_container = self.query_one("#bottom-app-container", Container)
7052
+ input_container.scroll_visible()
7053
+ except NoMatches:
7054
+ pass
7055
+ return
7056
+
6921
7057
  # Store message data for virtualization
6922
7058
  message_data = MessageData.from_widget(widget)
6923
7059
  if not widget.id:
@@ -6927,14 +7063,9 @@ class DeepAgentsApp(App):
6927
7063
  self._message_store.append(message_data)
6928
7064
  footer = self._build_message_timestamp_footer(message_data)
6929
7065
 
6930
- # Queued-message widgets must always stay at the bottom so they
6931
- # remain visually anchored below the current agent response.
6932
- if isinstance(widget, QueuedUserMessage):
6933
- await messages.mount(widget)
6934
- else:
6935
- await self._mount_before_queued(messages, widget)
6936
- if footer is not None:
6937
- await self._mount_before_queued(messages, footer)
7066
+ await self._mount_before_queued(messages, widget)
7067
+ if footer is not None:
7068
+ await self._mount_before_queued(messages, footer)
6938
7069
 
6939
7070
  # Prune old widgets if window exceeded
6940
7071
  await self._prune_old_messages()
@@ -7622,7 +7753,7 @@ class DeepAgentsApp(App):
7622
7753
  """
7623
7754
  if self._chat_input is None:
7624
7755
  return
7625
- self._chat_input.set_cursor_blink(blink=True)
7756
+ self._chat_input.set_cursor_blink(blink=self._cursor_blink_enabled)
7626
7757
  if isinstance(self.screen, ModalScreen):
7627
7758
  return
7628
7759
  if self._pending_approval_widget or self._pending_ask_user_widget:
@@ -9198,7 +9329,7 @@ class DeepAgentsApp(App):
9198
9329
  name=entry.name,
9199
9330
  transport=entry.transport,
9200
9331
  status="disabled",
9201
- error="Re-enabled — run `/mcp reconnect` to load.",
9332
+ error="Re-enabled — press Ctrl+R to load.",
9202
9333
  ),
9203
9334
  )
9204
9335
  self._mcp_server_info = updated
@@ -9995,7 +10126,7 @@ class DeepAgentsApp(App):
9995
10126
  messaging (which model couldn't be restored and what the session
9996
10127
  is falling back to) rather than the interactive `/model` errors.
9997
10128
  """
9998
- from deepagents_code.config import create_model, detect_provider, settings
10129
+ from deepagents_code.config import detect_provider, settings
9999
10130
  from deepagents_code.model_config import (
10000
10131
  ModelSpec,
10001
10132
  ProviderAuthState,
@@ -10121,7 +10252,8 @@ class DeepAgentsApp(App):
10121
10252
  display = f"{provider}:{model_name}"
10122
10253
 
10123
10254
  try:
10124
- result = create_model(
10255
+ result = await asyncio.to_thread(
10256
+ _create_model_with_deepagents_import_lock,
10125
10257
  display,
10126
10258
  extra_kwargs=extra_kwargs,
10127
10259
  profile_overrides=self._profile_override,
@@ -248,6 +248,11 @@ class AskUserMiddleware(AgentMiddleware[Any, ContextT, ResponseT]):
248
248
  questions=questions,
249
249
  tool_call_id=tool_call_id,
250
250
  )
251
+ # interrupt() raises GraphInterrupt from INSIDE tool execution,
252
+ # within ToolNode's wrap_tool_call chain. Any
253
+ # wrap_tool_call middleware that catches exceptions MUST re-raise
254
+ # GraphBubbleUp — a broad `except Exception` (e.g. ToolRetryMiddleware)
255
+ # would swallow this interrupt and silently break ask_user.
251
256
  response = interrupt(ask_request)
252
257
  return _parse_answers(response, questions, tool_call_id)
253
258
 
@@ -282,6 +282,23 @@ Includes both debug helpers (`/debug-error`) and recovery escape hatches
282
282
  (`/restart` — hot-respawn the app-owned LangGraph server).
283
283
  """
284
284
 
285
+ STARTUP_RECOVERY_COMMANDS: frozenset[str] = frozenset(
286
+ {"/install", "/reload", "/update"}
287
+ )
288
+ """`QUEUED`-tier commands that must still run when startup has failed.
289
+
290
+ When the configured model can't be built (e.g. its provider package is
291
+ missing) the server never starts and the app holds a `_server_startup_error`
292
+ state that parks queued messages. These are the recovery escape hatches for
293
+ that state — install the missing package, reload config/env, or upgrade the
294
+ tool — so they must bypass the queue rather than sit behind the very failure
295
+ they repair. `/model` and `/auth` already escape via `IMMEDIATE_UI` (which
296
+ opens a modal and defers the real work); the commands here instead perform
297
+ their repair work directly, so they stay `QUEUED` and rely on this exemption.
298
+ The bypass itself is gated in `_can_bypass_queue`. Every entry is also
299
+ `QUEUE_BOUND` — the recovery exemption is orthogonal to the normal queue.
300
+ """
301
+
285
302
  ALL_CLASSIFIED: frozenset[str] = (
286
303
  ALWAYS_IMMEDIATE
287
304
  | BYPASS_WHEN_CONNECTING
@@ -24,6 +24,11 @@ logger = logging.getLogger(__name__)
24
24
 
25
25
  _EXTRA_MARKER_RE = re.compile(r"""extra\s*==\s*["']([^"']+)["']""")
26
26
 
27
+
28
+ class ExtrasIntrospectionError(RuntimeError):
29
+ """Raised when installed extras cannot be determined safely."""
30
+
31
+
27
32
  _COMPOSITE_EXTRAS: frozenset[str] = frozenset({"all-providers", "all-sandboxes"})
28
33
  """Extras whose package set is already covered by other, more specific extras.
29
34
 
@@ -84,6 +89,31 @@ model-provider-drift checks; new extras must be added to the corresponding
84
89
  category frozenset above.
85
90
  """
86
91
 
92
+
93
+ def format_known_extras() -> str:
94
+ """Render the installable extras grouped by category as plain text.
95
+
96
+ Drives the no-argument `/install` slash-command help so users can
97
+ discover valid extras without consulting `pyproject.toml`. Sourced from
98
+ the category frozensets above, so it stays in sync with `KNOWN_EXTRAS`
99
+ automatically.
100
+
101
+ Returns:
102
+ Multi-line string with one labeled line per category, each listing
103
+ its extras alphabetically.
104
+ """
105
+ groups: tuple[tuple[str, frozenset[str]], ...] = (
106
+ ("Model providers", MODEL_PROVIDER_EXTRAS),
107
+ ("Sandboxes", SANDBOX_EXTRAS),
108
+ ("Other", STANDALONE_EXTRAS),
109
+ )
110
+ lines = ["Available extras:"]
111
+ lines.extend(
112
+ f" {label}: {', '.join(sorted(extras))}" for label, extras in groups if extras
113
+ )
114
+ return "\n".join(lines)
115
+
116
+
87
117
  ExtrasStatus = dict[str, list[tuple[str, str]]]
88
118
  """Mapping from extra name to `(package, installed_version)` tuples.
89
119
 
@@ -155,21 +185,55 @@ def get_extras_status(
155
185
  return result
156
186
 
157
187
 
188
+ def installed_extra_names(
189
+ distribution_name: str = "deepagents-code",
190
+ *,
191
+ strict: bool = False,
192
+ ) -> set[str]:
193
+ """Return extras with at least one installed dependency.
194
+
195
+ Args:
196
+ distribution_name: Name of the installed distribution to inspect.
197
+ strict: Raise when the distribution metadata cannot be read or parsed
198
+ reliably.
199
+
200
+ Returns:
201
+ Set of extra names whose optional dependency metadata has at least one
202
+ installed package. Composite extras are excluded.
203
+ """
204
+ statuses = get_optional_dependency_status(distribution_name, strict=strict)
205
+ return {extra.name for extra in statuses if extra.installed}
206
+
207
+
158
208
  def get_optional_dependency_status(
159
209
  distribution_name: str = "deepagents-code",
210
+ *,
211
+ strict: bool = False,
160
212
  ) -> tuple[ExtraDependencyStatus, ...]:
161
213
  """Return installed and missing optional dependencies grouped by extra.
162
214
 
163
215
  Args:
164
216
  distribution_name: Name of the installed distribution to inspect.
217
+ strict: Raise when the distribution metadata cannot be read or parsed
218
+ reliably.
165
219
 
166
220
  Returns:
167
221
  Sorted tuple of optional extra statuses. An empty tuple is returned
168
222
  when the distribution itself is not found.
223
+
224
+ Raises:
225
+ ExtrasIntrospectionError: If `strict` is `True` and metadata
226
+ introspection fails.
169
227
  """
170
228
  try:
171
229
  dist = distribution(distribution_name)
172
230
  except PackageNotFoundError:
231
+ if strict:
232
+ msg = (
233
+ f"Distribution {distribution_name!r} not found; cannot preserve "
234
+ "already-installed extras safely"
235
+ )
236
+ raise ExtrasIntrospectionError(msg) from None
173
237
  # Editable installs renamed by the user, dev checkouts without metadata,
174
238
  # or vendored copies all hit this path. The dependency screen otherwise
175
239
  # silently renders "none detected" twice; warn so the cause is visible.
@@ -186,6 +250,12 @@ def get_optional_dependency_status(
186
250
  try:
187
251
  req = Requirement(raw)
188
252
  except InvalidRequirement:
253
+ if strict:
254
+ msg = (
255
+ "Could not parse optional-dependency metadata; cannot "
256
+ f"preserve already-installed extras safely: {raw}"
257
+ )
258
+ raise ExtrasIntrospectionError(msg) from None
189
259
  logger.warning("Could not parse Requires-Dist entry: %s", raw)
190
260
  continue
191
261
  if not req.marker: