deepagents-code 0.1.19__tar.gz → 0.1.20__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 (285) hide show
  1. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/.gitignore +1 -0
  2. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/CHANGELOG.md +20 -0
  3. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/PKG-INFO +1 -1
  4. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_constants.py +9 -0
  5. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_session_stats.py +58 -28
  6. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_version.py +1 -1
  7. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/app.py +182 -14
  8. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/config.py +9 -1
  9. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/config_manifest.py +55 -11
  10. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/main.py +5 -3
  11. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/model_config.py +64 -0
  12. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/non_interactive.py +5 -2
  13. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/notifications.py +3 -0
  14. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/server.py +37 -4
  15. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/server_manager.py +5 -1
  16. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/sessions.py +16 -5
  17. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/textual_adapter.py +22 -9
  18. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/auth.py +149 -13
  19. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/install_confirm.py +24 -9
  20. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/model_selector.py +83 -42
  21. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/thread_selector.py +13 -0
  22. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/update_progress.py +2 -1
  23. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/pyproject.toml +1 -1
  24. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/scripts/install.sh +18 -9
  25. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_app.py +277 -8
  26. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_auth_widgets.py +257 -5
  27. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_config.py +58 -3
  28. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_config_manifest.py +111 -0
  29. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_install_command.py +42 -2
  30. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_install_confirm.py +19 -0
  31. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_install_script.py +77 -0
  32. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_main.py +9 -4
  33. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_model_config.py +103 -0
  34. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_model_selector.py +229 -0
  35. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_non_interactive.py +49 -0
  36. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_server.py +211 -0
  37. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_server_manager.py +36 -0
  38. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_session_stats.py +43 -7
  39. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_sessions.py +91 -0
  40. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_textual_adapter.py +124 -14
  41. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_thread_selector.py +15 -0
  42. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/uv.lock +1 -1
  43. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/AGENTS.md +0 -0
  44. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/COMMANDS.md +0 -0
  45. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/DEV.md +0 -0
  46. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/Makefile +0 -0
  47. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/README.md +0 -0
  48. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/THREAT_MODEL.md +0 -0
  49. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/__init__.py +0 -0
  50. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/__main__.py +0 -0
  51. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_ask_user_types.py +0 -0
  52. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_cli_context.py +0 -0
  53. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_debug.py +0 -0
  54. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_env_vars.py +0 -0
  55. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_git.py +0 -0
  56. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_server_config.py +0 -0
  57. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_startup_error.py +0 -0
  58. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_testing_models.py +0 -0
  59. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/_textual_patches.py +0 -0
  60. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/agent.py +0 -0
  61. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/app.tcss +0 -0
  62. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/ask_user.py +0 -0
  63. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/auth_commands.py +0 -0
  64. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/auth_display.py +0 -0
  65. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/auth_store.py +0 -0
  66. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/built_in_skills/__init__.py +0 -0
  67. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
  68. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
  69. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  70. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  71. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/clipboard.py +0 -0
  72. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/command_registry.py +0 -0
  73. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/config_commands.py +0 -0
  74. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/configurable_model.py +0 -0
  75. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/default_agent_prompt.md +0 -0
  76. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/editor.py +0 -0
  77. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/event_bus.py +0 -0
  78. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/extras_info.py +0 -0
  79. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/file_ops.py +0 -0
  80. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/filesystem_empty_result.py +0 -0
  81. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/formatting.py +0 -0
  82. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/hooks.py +0 -0
  83. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/input.py +0 -0
  84. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/integrations/__init__.py +0 -0
  85. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/integrations/openai_codex.py +0 -0
  86. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/integrations/sandbox_config.py +0 -0
  87. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/integrations/sandbox_factory.py +0 -0
  88. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/integrations/sandbox_provider.py +0 -0
  89. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/integrations/sandbox_registry.py +0 -0
  90. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/iterm_cursor_guide.py +0 -0
  91. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/local_context.py +0 -0
  92. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/managed_tools.py +0 -0
  93. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_auth.py +0 -0
  94. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_commands.py +0 -0
  95. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_disabled.py +0 -0
  96. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_login_service.py +0 -0
  97. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_oauth_ui.py +0 -0
  98. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_providers/__init__.py +0 -0
  99. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_providers/_registry.py +0 -0
  100. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_providers/base.py +0 -0
  101. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_providers/github.py +0 -0
  102. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_providers/slack.py +0 -0
  103. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_tools.py +0 -0
  104. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/mcp_trust.py +0 -0
  105. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/media_utils.py +0 -0
  106. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/memory_guard.py +0 -0
  107. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/offload.py +0 -0
  108. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/onboarding.py +0 -0
  109. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/output.py +0 -0
  110. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/project_utils.py +0 -0
  111. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/py.typed +0 -0
  112. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/remote_client.py +0 -0
  113. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/resume_state.py +0 -0
  114. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/server_graph.py +0 -0
  115. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/skills/__init__.py +0 -0
  116. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/skills/commands.py +0 -0
  117. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/skills/invocation.py +0 -0
  118. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/skills/load.py +0 -0
  119. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/state_migration.py +0 -0
  120. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/subagents.py +0 -0
  121. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/system_prompt.md +0 -0
  122. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/terminal_capabilities.py +0 -0
  123. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/terminal_escape.py +0 -0
  124. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/theme.py +0 -0
  125. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/tool_display.py +0 -0
  126. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/tools.py +0 -0
  127. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/ui.py +0 -0
  128. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/unicode_security.py +0 -0
  129. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/update_check.py +0 -0
  130. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/__init__.py +0 -0
  131. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/_links.py +0 -0
  132. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/agent_selector.py +0 -0
  133. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/approval.py +0 -0
  134. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/ask_user.py +0 -0
  135. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/autocomplete.py +0 -0
  136. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/chat_input.py +0 -0
  137. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/codex_auth.py +0 -0
  138. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/cwd_switch.py +0 -0
  139. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/diff.py +0 -0
  140. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/history.py +0 -0
  141. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/launch_init.py +0 -0
  142. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/loading.py +0 -0
  143. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/mcp_login.py +0 -0
  144. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/mcp_reconnect.py +0 -0
  145. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/mcp_viewer.py +0 -0
  146. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/message_store.py +0 -0
  147. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/messages.py +0 -0
  148. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/notification_center.py +0 -0
  149. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/notification_detail.py +0 -0
  150. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/notification_settings.py +0 -0
  151. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/restart_prompt.py +0 -0
  152. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/status.py +0 -0
  153. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/theme_selector.py +0 -0
  154. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/tool_renderers.py +0 -0
  155. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/tool_widgets.py +0 -0
  156. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/update_available.py +0 -0
  157. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/deepagents_code/widgets/welcome.py +0 -0
  158. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/examples/skills/arxiv-search/SKILL.md +0 -0
  159. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  160. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/examples/skills/langgraph-docs/SKILL.md +0 -0
  161. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/examples/skills/skill-creator/SKILL.md +0 -0
  162. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  163. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  164. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/examples/skills/web-research/SKILL.md +0 -0
  165. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/images/tui.png +0 -0
  166. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/scripts/check_imports.py +0 -0
  167. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/scripts/debug_server.sh +0 -0
  168. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/scripts/generate_commands_catalog.py +0 -0
  169. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/README.md +0 -0
  170. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/__init__.py +0 -0
  171. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/benchmarks/__init__.py +0 -0
  172. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  173. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  174. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/conftest.py +0 -0
  175. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/test_acp_mode.py +0 -0
  176. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/test_compact_resume.py +0 -0
  177. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/test_sandbox_factory.py +0 -0
  178. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/integration_tests/test_sandbox_operations.py +0 -0
  179. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/__init__.py +0 -0
  180. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/conftest.py +0 -0
  181. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/skills/__init__.py +0 -0
  182. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/skills/test_commands.py +0 -0
  183. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/skills/test_load.py +0 -0
  184. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/skills/test_skills_json.py +0 -0
  185. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_agent.py +0 -0
  186. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_agent_friendly.py +0 -0
  187. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_agent_selector.py +0 -0
  188. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_approval.py +0 -0
  189. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_args.py +0 -0
  190. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_ask_user.py +0 -0
  191. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  192. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_auth_commands.py +0 -0
  193. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_auth_display.py +0 -0
  194. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_auth_store.py +0 -0
  195. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_autocomplete.py +0 -0
  196. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_charset.py +0 -0
  197. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_chat_input.py +0 -0
  198. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_clipboard.py +0 -0
  199. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_command_registry.py +0 -0
  200. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_compact_tool.py +0 -0
  201. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_configurable_model.py +0 -0
  202. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_cursor_blink.py +0 -0
  203. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_cwd_switch.py +0 -0
  204. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_debug.py +0 -0
  205. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_editor.py +0 -0
  206. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_end_to_end.py +0 -0
  207. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_env_vars.py +0 -0
  208. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_event_bus.py +0 -0
  209. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_exception_handling.py +0 -0
  210. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_extras_info.py +0 -0
  211. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_file_ops.py +0 -0
  212. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_filesystem_empty_result.py +0 -0
  213. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_formatting.py +0 -0
  214. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_git.py +0 -0
  215. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_history.py +0 -0
  216. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_hooks.py +0 -0
  217. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_imports.py +0 -0
  218. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_input_parsing.py +0 -0
  219. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
  220. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_launch_init.py +0 -0
  221. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_links.py +0 -0
  222. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_loading.py +0 -0
  223. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_local_context.py +0 -0
  224. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_main_acp_mode.py +0 -0
  225. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_main_args.py +0 -0
  226. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_managed_tools.py +0 -0
  227. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_auth.py +0 -0
  228. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_commands.py +0 -0
  229. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_disabled.py +0 -0
  230. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_login_modal.py +0 -0
  231. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_login_service.py +0 -0
  232. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
  233. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_reconnect.py +0 -0
  234. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_tools.py +0 -0
  235. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_trust.py +0 -0
  236. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_mcp_viewer.py +0 -0
  237. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_media_utils.py +0 -0
  238. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_memory_guard.py +0 -0
  239. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_message_store.py +0 -0
  240. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_messages.py +0 -0
  241. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_model_switch.py +0 -0
  242. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_notification_center.py +0 -0
  243. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_notification_detail.py +0 -0
  244. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_notifications.py +0 -0
  245. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_offload.py +0 -0
  246. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  247. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_onboarding.py +0 -0
  248. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_openai_codex_integration.py +0 -0
  249. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_output.py +0 -0
  250. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_reload.py +0 -0
  251. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_remote_client.py +0 -0
  252. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_restart_prompt.py +0 -0
  253. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_resume_state.py +0 -0
  254. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_sandbox_config.py +0 -0
  255. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_sandbox_factory.py +0 -0
  256. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_sandbox_provider.py +0 -0
  257. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_sandbox_registry.py +0 -0
  258. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_server_config.py +0 -0
  259. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_server_graph.py +0 -0
  260. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_server_helpers.py +0 -0
  261. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_shell_allow_list.py +0 -0
  262. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_skill_invocation.py +0 -0
  263. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_startup_fast_paths.py +0 -0
  264. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_state_migration.py +0 -0
  265. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_status.py +0 -0
  266. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_subagents.py +0 -0
  267. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  268. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_terminal_escape.py +0 -0
  269. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_terminal_progress_preference.py +0 -0
  270. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_textual_patches.py +0 -0
  271. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_theme.py +0 -0
  272. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_tool_display.py +0 -0
  273. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_tracing_replicas.py +0 -0
  274. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_ui.py +0 -0
  275. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_unicode_security.py +0 -0
  276. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_update_available.py +0 -0
  277. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_update_check.py +0 -0
  278. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_update_progress.py +0 -0
  279. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_version.py +0 -0
  280. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/test_welcome.py +0 -0
  281. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/tools/__init__.py +0 -0
  282. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/tools/test_current_thread.py +0 -0
  283. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/tools/test_fetch_url.py +0 -0
  284. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/widgets/__init__.py +0 -0
  285. {deepagents_code-0.1.19 → deepagents_code-0.1.20}/tests/unit_tests/widgets/test_diff.py +0 -0
@@ -100,6 +100,7 @@ ipython_config.py
100
100
  # This is especially recommended for binary packages to ensure reproducibility, and is more
101
101
  # commonly ignored for libraries.
102
102
  #uv.lock
103
+ .local_deps/
103
104
 
104
105
  # poetry
105
106
  # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
@@ -2,6 +2,26 @@
2
2
 
3
3
  # Deep Agents Code Changelog
4
4
 
5
+ ## [0.1.20](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.19...deepagents-code==0.1.20) (2026-06-17)
6
+
7
+ ### Features
8
+
9
+ * Manage Tavily web-search API key in `/auth` ([#4062](https://github.com/langchain-ai/deepagents/issues/4062)) ([90cc099](https://github.com/langchain-ai/deepagents/commit/90cc099b0998a9aa55df01e3bc12ad1597c65365))
10
+ * Surface uninstalled known providers in `/auth` menu ([#4059](https://github.com/langchain-ai/deepagents/issues/4059)) ([73db271](https://github.com/langchain-ai/deepagents/commit/73db2719bce5ad3c933ffee643df4a62cfd60e17))
11
+ * Show provider in usage stats table ([#4049](https://github.com/langchain-ai/deepagents/issues/4049)) ([309b054](https://github.com/langchain-ai/deepagents/commit/309b054fdd040adb5086408a3bcb2550ba73439e))
12
+ * Quiet install script's full dependency list ([#4058](https://github.com/langchain-ai/deepagents/issues/4058)) ([de2c9fd](https://github.com/langchain-ai/deepagents/commit/de2c9fd8c7437fdcdfe738599f83808c5423c55c))
13
+
14
+ ### Bug Fixes
15
+
16
+ * Read `LANGSMITH_PROJECT` for `tracing.langsmith_project` + show default ([#4054](https://github.com/langchain-ai/deepagents/issues/4054)) ([fec1551](https://github.com/langchain-ai/deepagents/commit/fec1551e901a4fe88bd74ee4833a91ef93b1e93f))
17
+ * Surface recommended models missing from installed provider profiles ([#4057](https://github.com/langchain-ai/deepagents/issues/4057)) ([56e0d31](https://github.com/langchain-ai/deepagents/commit/56e0d31b6c507d5a1e95c3f2058a8d3b9488a4db))
18
+ * Clarify post-update restart semantics ([#4046](https://github.com/langchain-ai/deepagents/issues/4046)) ([6318b81](https://github.com/langchain-ai/deepagents/commit/6318b817399f7dd2f2e6dc07987dca46f1da9886))
19
+ * Hide `[SYSTEM]` interrupt notices from `/threads` prompt ([#3988](https://github.com/langchain-ai/deepagents/issues/3988)) ([68fa0cb](https://github.com/langchain-ai/deepagents/commit/68fa0cb4d48ff3f3e552bbf16f8316433c7b6f34))
20
+ * Keep install-required `/model` rows dimmed after navigation ([#4048](https://github.com/langchain-ai/deepagents/issues/4048)) ([c499634](https://github.com/langchain-ai/deepagents/commit/c499634c530dcab9b18e3126b4573ff0d36c3efa))
21
+ * Rescaffold server workspace on `/restart` when config is missing ([#4050](https://github.com/langchain-ai/deepagents/issues/4050)) ([de51b0e](https://github.com/langchain-ai/deepagents/commit/de51b0e082293f5f795d96d3bf717c82efb9337e))
22
+ * Remove transient `Restarting server...` message after restart ([#4047](https://github.com/langchain-ai/deepagents/issues/4047)) ([87cc504](https://github.com/langchain-ai/deepagents/commit/87cc50428bc96ef11dfb11cee4020bff322a7216))
23
+ * Suppress model-switch defer toast on bare reconnect ([#4060](https://github.com/langchain-ai/deepagents/issues/4060)) ([60b1052](https://github.com/langchain-ai/deepagents/commit/60b10520d24c87b9c8c9d85b94cffae9e3c67c43))
24
+
5
25
  ## [0.1.19](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.18...deepagents-code==0.1.19) (2026-06-17)
6
26
 
7
27
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents-code
3
- Version: 0.1.19
3
+ Version: 0.1.20
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/
@@ -12,3 +12,12 @@ from typing import Final
12
12
 
13
13
  DEFAULT_AGENT_NAME: Final[str] = "agent"
14
14
  """Default agent / assistant identifier when no `-a` flag is given."""
15
+
16
+ SYSTEM_MESSAGE_PREFIX: Final[str] = "[SYSTEM]"
17
+ """Prefix for synthetic human messages (e.g. interrupt cancellation notices).
18
+
19
+ Such messages are written to the `messages` channel for the agent's benefit on
20
+ resume but are not user-authored, so they are filtered out of both the rendered
21
+ transcript and a thread's initial prompt. Shared here so the single producer
22
+ (`textual_adapter`) and its consumers (`app`, `sessions`) agree on one literal.
23
+ """
@@ -17,64 +17,91 @@ SpinnerStatus = Literal["Thinking", "Offloading", "Loading thread"] | None
17
17
 
18
18
  @dataclass
19
19
  class ModelStats:
20
- """Token stats for a single model within a session.
21
-
22
- Attributes:
23
- request_count: Number of LLM API requests made to this model.
24
- input_tokens: Cumulative input tokens sent to this model.
25
- output_tokens: Cumulative output tokens received from this model.
26
- """
20
+ """Token stats for a single model within a session."""
27
21
 
28
22
  request_count: int = 0
23
+ """Number of LLM API requests made to this model."""
24
+
29
25
  input_tokens: int = 0
26
+ """Cumulative input tokens sent to this model."""
27
+
30
28
  output_tokens: int = 0
29
+ """Cumulative output tokens received from this model."""
30
+
31
+ provider: str = ""
32
+ """Provider that served this model (e.g. `openai`), or `""` when unknown."""
33
+
34
+ model_name: str = ""
35
+ """Model name displayed in usage output."""
36
+
37
+
38
+ ModelStatsKey = tuple[str, str]
39
+ """Per-model dict key: the `(provider, model_name)` pair.
40
+
41
+ Pairing the provider with the model name keeps the same model served by
42
+ different providers (e.g. `gpt-5.5` via `openai` vs `azure`) in separate rows
43
+ instead of collapsing them. The key is always built from the same values stored
44
+ on the corresponding `ModelStats`, so key and fields never diverge.
45
+ """
31
46
 
32
47
 
33
48
  @dataclass
34
49
  class SessionStats:
35
- """Stats accumulated over a single agent turn (or full session).
36
-
37
- Attributes:
38
- request_count: Total LLM API requests made (each chunk with
39
- usage_metadata counts as one completed request).
40
- input_tokens: Cumulative input tokens across all LLM requests.
41
- output_tokens: Cumulative output tokens across all LLM requests.
42
- wall_time_seconds: Wall-clock duration from stream start to end.
43
- per_model: Per-model breakdown keyed by model name.
44
- Populated only when `record_request` receives a non-empty
45
- `model_name`. Empty dict means no named-model requests were
46
- recorded; `print_usage_table` omits the model table in that case and
47
- shows only the wall-time line (if applicable).
48
- """
50
+ """Stats accumulated over a single agent turn (or full session)."""
49
51
 
50
52
  request_count: int = 0
53
+ """Total LLM API requests made.
54
+
55
+ Each chunk with `usage_metadata` counts as one completed request.
56
+ """
57
+
51
58
  input_tokens: int = 0
59
+ """Cumulative input tokens across all LLM requests."""
60
+
52
61
  output_tokens: int = 0
62
+ """Cumulative output tokens across all LLM requests."""
63
+
53
64
  wall_time_seconds: float = 0.0
54
- per_model: dict[str, ModelStats] = field(default_factory=dict)
65
+ """Wall-clock duration from stream start to end."""
66
+
67
+ per_model: dict[ModelStatsKey, ModelStats] = field(default_factory=dict)
68
+ """Per-model breakdown keyed by `(provider, model_name)`.
69
+
70
+ Populated only when `record_request` receives a non-empty `model_name`. Empty
71
+ dict means no named-model requests were recorded; `print_usage_table` omits
72
+ the model table in that case and shows only the wall-time line (if applicable).
73
+ """
55
74
 
56
75
  def record_request(
57
76
  self,
58
77
  model_name: str,
59
78
  input_toks: int,
60
79
  output_toks: int,
80
+ provider: str = "",
61
81
  ) -> None:
62
82
  """Accumulate token counts for one completed LLM request.
63
83
 
64
84
  Updates both the session totals and the per-model breakdown.
65
85
 
66
86
  Args:
67
- model_name: The model that served this request (used as the
68
- per-model key). Pass an empty string to skip the per-model
69
- breakdown for this request.
87
+ model_name: The model that served this request. Combined with
88
+ `provider` to form the per-model key. Pass an empty string to
89
+ skip the per-model breakdown for this request.
70
90
  input_toks: Input tokens for this request.
71
91
  output_toks: Output tokens for this request.
92
+ provider: Provider that served the model (e.g. `openai`). Combined
93
+ with `model_name` to form the per-model key, so the same model
94
+ served by different providers is tracked separately.
72
95
  """
73
96
  self.request_count += 1
74
97
  self.input_tokens += input_toks
75
98
  self.output_tokens += output_toks
76
99
  if model_name:
77
- entry = self.per_model.setdefault(model_name, ModelStats())
100
+ key = (provider, model_name)
101
+ entry = self.per_model.setdefault(
102
+ key,
103
+ ModelStats(provider=provider, model_name=model_name),
104
+ )
78
105
  entry.request_count += 1
79
106
  entry.input_tokens += input_toks
80
107
  entry.output_tokens += output_toks
@@ -91,8 +118,11 @@ class SessionStats:
91
118
  self.input_tokens += other.input_tokens
92
119
  self.output_tokens += other.output_tokens
93
120
  self.wall_time_seconds += other.wall_time_seconds
94
- for model, ms in other.per_model.items():
95
- entry = self.per_model.setdefault(model, ModelStats())
121
+ for key, ms in other.per_model.items():
122
+ entry = self.per_model.setdefault(
123
+ key,
124
+ ModelStats(provider=ms.provider, model_name=ms.model_name),
125
+ )
96
126
  entry.request_count += ms.request_count
97
127
  entry.input_tokens += ms.input_tokens
98
128
  entry.output_tokens += ms.output_tokens
@@ -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.19" # x-release-please-version
5
+ __version__ = "0.1.20" # 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."""
@@ -42,7 +42,10 @@ from deepagents_code import (
42
42
  theme,
43
43
  )
44
44
  from deepagents_code._cli_context import CLIContext
45
- from deepagents_code._constants import DEFAULT_AGENT_NAME as DEFAULT_ASSISTANT_ID
45
+ from deepagents_code._constants import (
46
+ DEFAULT_AGENT_NAME as DEFAULT_ASSISTANT_ID,
47
+ SYSTEM_MESSAGE_PREFIX,
48
+ )
46
49
  from deepagents_code._git import (
47
50
  read_git_branch_from_filesystem,
48
51
  read_git_branch_via_subprocess,
@@ -161,6 +164,32 @@ def _create_model_with_deepagents_import_lock(
161
164
  )
162
165
 
163
166
 
167
+ def _extra_is_ready(extra: str) -> bool | None:
168
+ """Return whether all dependencies for `extra` are installed.
169
+
170
+ Returns:
171
+ `True` when every package declared by `extra` is importable, `False`
172
+ when one or more are missing, or `None` when the extra metadata
173
+ can't be introspected — an unknown state, distinct from a negative
174
+ one, so callers don't treat "couldn't check" as "not installed".
175
+ """
176
+ from deepagents_code.extras_info import (
177
+ ExtrasIntrospectionError,
178
+ get_optional_dependency_status,
179
+ )
180
+
181
+ try:
182
+ statuses = get_optional_dependency_status(strict=True)
183
+ except ExtrasIntrospectionError:
184
+ logger.warning(
185
+ "Could not verify whether extra %r is installed",
186
+ extra,
187
+ exc_info=True,
188
+ )
189
+ return None
190
+ return any(status.name == extra and status.ready for status in statuses)
191
+
192
+
164
193
  @dataclass(frozen=True)
165
194
  class _ConfigWriteResult:
166
195
  """Result of a config write with TUI-facing failure context."""
@@ -3684,7 +3713,8 @@ class DeepAgentsApp(App):
3684
3713
  self.notify(
3685
3714
  f"Update available: v{latest}{release_age}. "
3686
3715
  f"Currently installed: {cli_version}{installed_age}. "
3687
- "Restart dcode to install the update automatically.",
3716
+ "Quit and relaunch dcode to install the update "
3717
+ "automatically.",
3688
3718
  severity="information",
3689
3719
  timeout=12,
3690
3720
  markup=False,
@@ -3924,7 +3954,9 @@ class DeepAgentsApp(App):
3924
3954
  self._update_available = (False, None)
3925
3955
  await self._mount_message(
3926
3956
  AppMessage(
3927
- f"Updated to v{latest}. Restart to use the new version."
3957
+ f"Updated to v{latest}. Quit and relaunch dcode to use "
3958
+ "the new version (`/restart` only restarts the server, "
3959
+ "not the CLI)."
3928
3960
  ),
3929
3961
  )
3930
3962
  else:
@@ -4727,6 +4759,35 @@ class DeepAgentsApp(App):
4727
4759
  return
4728
4760
  await container.mount(widget)
4729
4761
 
4762
+ async def _mount_transient_app_message(self, content: str) -> AppMessage | None:
4763
+ """Mount an `AppMessage` that is not tracked by the message store.
4764
+
4765
+ Use for status text that should disappear once the state it describes
4766
+ resolves (e.g. "Restarting server..."). The returned widget can be
4767
+ removed directly; nothing lingers in the store to re-hydrate later.
4768
+
4769
+ Args:
4770
+ content: The message text to display.
4771
+
4772
+ Returns:
4773
+ The mounted widget, or `None` when the messages container is
4774
+ missing or detached.
4775
+ """
4776
+ try:
4777
+ messages = self.query_one("#messages", Container)
4778
+ except (NoMatches, ScreenStackError):
4779
+ logger.debug(
4780
+ "Messages container unavailable; skipping transient status %r",
4781
+ content,
4782
+ exc_info=True,
4783
+ )
4784
+ return None
4785
+ if not messages.is_attached:
4786
+ return None
4787
+ widget = AppMessage(content)
4788
+ await self._mount_before_queued(messages, widget)
4789
+ return widget
4790
+
4730
4791
  def _is_spinner_at_correct_position(self, container: Container) -> bool:
4731
4792
  """Check whether the loading spinner is already correctly positioned.
4732
4793
 
@@ -7528,7 +7589,7 @@ class DeepAgentsApp(App):
7528
7589
  content = (
7529
7590
  msg.content if isinstance(msg.content, str) else str(msg.content)
7530
7591
  )
7531
- if content.startswith("[SYSTEM]"):
7592
+ if content.startswith(SYSTEM_MESSAGE_PREFIX):
7532
7593
  continue
7533
7594
 
7534
7595
  # Detect skill invocations persisted via additional_kwargs
@@ -8951,6 +9012,13 @@ class DeepAgentsApp(App):
8951
9012
  ) -> None:
8952
9013
  """Switch to `model_spec` now, or defer until in-flight work finishes.
8953
9014
 
9015
+ The deferral toast is shown only for genuine in-flight user work
9016
+ (`_agent_running`/`_shell_running`). A switch deferred solely because the
9017
+ server is reconnecting (`_connecting` — e.g. the transient restart during
9018
+ install-then-switch) drains automatically once the server is ready and is
9019
+ already confirmed by the following "Switched to ..." message, so the
9020
+ "after current task completes" toast there is misleading noise.
9021
+
8954
9022
  Args:
8955
9023
  model_spec: The `provider:model` spec to switch to.
8956
9024
  extra_kwargs: Extra constructor kwargs from `--model-params`.
@@ -8968,10 +9036,11 @@ class DeepAgentsApp(App):
8968
9036
  ),
8969
9037
  ),
8970
9038
  )
8971
- self.notify(
8972
- "Model will switch after current task completes.",
8973
- timeout=3,
8974
- )
9039
+ if self._agent_running or self._shell_running:
9040
+ self.notify(
9041
+ "Model will switch after current task completes.",
9042
+ timeout=3,
9043
+ )
8975
9044
  else:
8976
9045
  self.call_later(
8977
9046
  partial(
@@ -9186,10 +9255,53 @@ class DeepAgentsApp(App):
9186
9255
  def handle_result(_result: None) -> None:
9187
9256
  if self._chat_input:
9188
9257
  self._chat_input.focus_input()
9258
+ # When the user selected a greyed-out (uninstalled) provider and
9259
+ # confirmed installing it, install the extra and reopen the manager
9260
+ # so they can add a key against the now-installed provider.
9261
+ extra = screen.pending_install_extra
9262
+ if extra is not None:
9263
+ from functools import partial
9264
+
9265
+ self.call_later(
9266
+ partial(self._install_provider_then_reopen_auth, extra),
9267
+ )
9268
+ return
9189
9269
  task = asyncio.create_task(self._maybe_start_deferred_server_from_default())
9190
9270
  task.add_done_callback(_log_task_exception)
9191
9271
 
9192
- self.push_screen(AuthManagerScreen(), handle_result)
9272
+ screen = AuthManagerScreen()
9273
+ self.push_screen(screen, handle_result)
9274
+
9275
+ async def _install_provider_then_reopen_auth(self, extra: str) -> None:
9276
+ """Install a provider's extra from `/auth`, then reopen the manager.
9277
+
9278
+ Args:
9279
+ extra: The extra that installs the selected provider's integration.
9280
+ """
9281
+ if await self._install_extra(extra, auto_restart=True):
9282
+ await self._show_auth_manager()
9283
+ return
9284
+ # `_install_extra` returns `False` both when the install genuinely
9285
+ # failed (it already surfaced the reason) and when the package landed
9286
+ # but the server restart didn't. Adding a key doesn't need the restart,
9287
+ # so reopen whenever the extra is importable; only stay in chat on a
9288
+ # real failure the user has already seen explained.
9289
+ ready = await asyncio.to_thread(_extra_is_ready, extra)
9290
+ if ready:
9291
+ from deepagents_code.model_config import clear_caches
9292
+
9293
+ clear_caches()
9294
+ await self._show_auth_manager()
9295
+ return
9296
+ if ready is None:
9297
+ # Introspection couldn't confirm the state (rare). Don't dead-end
9298
+ # silently after a multi-step flow — point the user back to `/auth`.
9299
+ await self._mount_message(
9300
+ AppMessage(
9301
+ f"Couldn't verify whether '{extra}' finished installing. "
9302
+ "Reopen `/auth` to add a key once it has.",
9303
+ ),
9304
+ )
9193
9305
 
9194
9306
  def _switch_agent(self, agent_name: str) -> None:
9195
9307
  """Switch to a different agent and hot-restart the backing server.
@@ -9982,8 +10094,49 @@ class DeepAgentsApp(App):
9982
10094
  markup=False,
9983
10095
  )
9984
10096
  return
10097
+ if action_id == ActionId.ENTER_API_KEY:
10098
+ await self._enter_service_api_key(entry, payload)
10099
+ return
9985
10100
  self._log_unknown_action(entry, action_id)
9986
10101
 
10102
+ async def _enter_service_api_key(
10103
+ self,
10104
+ entry: PendingNotification,
10105
+ payload: MissingDepPayload,
10106
+ ) -> None:
10107
+ """Open the API-key entry prompt (the one `/auth` uses) for a service.
10108
+
10109
+ Lets the user store a service API key inline instead of exporting an
10110
+ env var before launch.
10111
+
10112
+ Args:
10113
+ entry: The missing-dependency notification entry.
10114
+ payload: Typed payload carrying the service (tool) name.
10115
+ """
10116
+ from deepagents_code.model_config import SERVICE_API_KEY_ENV
10117
+
10118
+ service = payload.tool
10119
+ # `env_var is None` covers any non-service tool, since `is_service` is
10120
+ # exactly membership in `SERVICE_API_KEY_ENV`.
10121
+ env_var = SERVICE_API_KEY_ENV.get(service)
10122
+ if env_var is None:
10123
+ self._log_unknown_action(entry, ActionId.ENTER_API_KEY)
10124
+ return
10125
+
10126
+ from deepagents_code.widgets.auth import AuthPromptScreen, AuthResult
10127
+
10128
+ result = await self._push_screen_wait(
10129
+ AuthPromptScreen(service, env_var),
10130
+ )
10131
+ if result == AuthResult.SAVED:
10132
+ self._notice_registry.remove(entry.key)
10133
+ self.notify(
10134
+ f"Saved {service} API key. Restart to apply.",
10135
+ severity="information",
10136
+ timeout=6,
10137
+ markup=False,
10138
+ )
10139
+
9987
10140
  async def _handle_update_action(
9988
10141
  self,
9989
10142
  entry: PendingNotification,
@@ -10058,7 +10211,8 @@ class DeepAgentsApp(App):
10058
10211
  if not progress_modal_visible:
10059
10212
  self.notify(
10060
10213
  f"Updated to v{payload.latest}. "
10061
- "Restart to use the new version.",
10214
+ "Quit and relaunch dcode to use the new version "
10215
+ "(/restart only restarts the server, not the CLI).",
10062
10216
  severity="information",
10063
10217
  timeout=10,
10064
10218
  markup=False,
@@ -11081,8 +11235,15 @@ class DeepAgentsApp(App):
11081
11235
  return False
11082
11236
  if not await self._reload_configuration_for_restart():
11083
11237
  return False
11084
- await self._mount_message(AppMessage("Restarting server..."))
11085
- if not await self._restart_server_manual():
11238
+ restarting = await self._mount_transient_app_message("Restarting server...")
11239
+ restarted = False
11240
+ try:
11241
+ restarted = await self._restart_server_manual()
11242
+ finally:
11243
+ if restarting is not None:
11244
+ with suppress(NoMatches, ScreenStackError):
11245
+ await restarting.remove()
11246
+ if not restarted:
11086
11247
  return False
11087
11248
  await self._mount_message(AppMessage("Restart complete."))
11088
11249
  return True
@@ -11202,8 +11363,15 @@ class DeepAgentsApp(App):
11202
11363
  )
11203
11364
  return
11204
11365
 
11205
- await self._mount_message(AppMessage("Restarting server..."))
11206
- if await self._restart_server_manual():
11366
+ restarting = await self._mount_transient_app_message("Restarting server...")
11367
+ restarted = False
11368
+ try:
11369
+ restarted = await self._restart_server_manual()
11370
+ finally:
11371
+ if restarting is not None:
11372
+ with suppress(NoMatches, ScreenStackError):
11373
+ await restarting.remove()
11374
+ if restarted:
11207
11375
  await self._mount_message(AppMessage("Restart complete."))
11208
11376
 
11209
11377
  async def _restart_server_manual(self) -> bool:
@@ -549,6 +549,13 @@ def _ensure_bootstrap() -> None:
549
549
  # Tracing enabled without a key floods the TUI with 401 ingest
550
550
  # errors; disable it before any traced run starts.
551
551
  _disable_orphaned_tracing()
552
+
553
+ # Bridge stored service keys (e.g. Tavily web search, entered via
554
+ # `/auth`) onto their canonical env vars before settings detection,
555
+ # so a stored key activates the feature without exporting the var.
556
+ from deepagents_code.model_config import apply_stored_service_credentials
557
+
558
+ apply_stored_service_credentials()
552
559
  except Exception:
553
560
  logger.exception(
554
561
  "Bootstrap failed; .env values and LANGSMITH_PROJECT override "
@@ -2378,6 +2385,7 @@ def get_langsmith_project_name() -> str | None:
2378
2385
  Returns:
2379
2386
  Project name string when LangSmith tracing is active, None otherwise.
2380
2387
  """
2388
+ from deepagents_code.config_manifest import LANGSMITH_PROJECT_DEFAULT
2381
2389
  from deepagents_code.model_config import resolve_env_var
2382
2390
 
2383
2391
  langsmith_key = resolve_env_var("LANGSMITH_API_KEY") or resolve_env_var(
@@ -2392,7 +2400,7 @@ def get_langsmith_project_name() -> str | None:
2392
2400
  return (
2393
2401
  _get_settings().deepagents_langchain_project
2394
2402
  or os.environ.get("LANGSMITH_PROJECT")
2395
- or "deepagents-code"
2403
+ or LANGSMITH_PROJECT_DEFAULT
2396
2404
  )
2397
2405
 
2398
2406
 
@@ -58,6 +58,12 @@ INTERPRETER_MAX_RESULT_CHARS_DEFAULT = 4000
58
58
  INTERPRETER_PTC_DEFAULT: str | bool | list[str] = False
59
59
  INTERPRETER_PTC_ACKNOWLEDGE_UNSAFE_DEFAULT = False
60
60
 
61
+ LANGSMITH_PROJECT_DEFAULT = "deepagents-code"
62
+ """Project agent traces fall back to when no project env var is set.
63
+
64
+ Single source of truth shared by the `tracing.langsmith_project` option and
65
+ `config.get_langsmith_project_name`."""
66
+
61
67
 
62
68
  class OptionKind(Enum):
63
69
  """How an option's raw env/TOML value is coerced to a typed value.
@@ -163,6 +169,14 @@ class ConfigOption:
163
169
  `DEEPAGENTS_CODE_` prefix override is applied dynamically at resolution time.
164
170
  """
165
171
 
172
+ fallback_env_vars: tuple[str, ...] = ()
173
+ """Secondary env vars read (in order) when `env_var` is unset.
174
+
175
+ Read literally — no `DEEPAGENTS_CODE_` prefix logic — so `config show`/`get`
176
+ mirror runtime fallbacks such as `get_langsmith_project_name` reading bare
177
+ `LANGSMITH_PROJECT`.
178
+ """
179
+
166
180
  toml_keys: tuple[str, ...] | None = None
167
181
  """Section/key path within `config.toml`, or `None`."""
168
182
 
@@ -207,9 +221,24 @@ class ConfigOption:
207
221
  `resolve_scalar`. Catching it here fails the import (and the test suite).
208
222
 
209
223
  Raises:
210
- TypeError: When `default` is mutable, a `STRUCTURED` option declares
211
- a default, or a scalar option's default has the wrong type.
224
+ TypeError: When `fallback_env_vars` is not a tuple of non-empty
225
+ strings, `default` is mutable, a `STRUCTURED` option declares a
226
+ default, or a scalar option's default has the wrong type.
212
227
  """
228
+ # Guard `fallback_env_vars` independently of `default` (which has its own
229
+ # early-return path below): like `default`, it is shared by reference
230
+ # through the `get_config_options` `lru_cache`, so a mutable value (a
231
+ # `list`) would reintroduce the aliasing hazard the default guard exists
232
+ # to prevent. Empty names never match any env var, so reject those too.
233
+ if not isinstance(self.fallback_env_vars, tuple) or any(
234
+ not isinstance(name, str) or not name for name in self.fallback_env_vars
235
+ ):
236
+ msg = (
237
+ f"{self.key}: fallback_env_vars must be a tuple of non-empty "
238
+ f"strings, got {self.fallback_env_vars!r}"
239
+ )
240
+ raise TypeError(msg)
241
+
213
242
  default = self.default
214
243
  if default is None:
215
244
  if self.invert_toml_bool:
@@ -498,27 +527,40 @@ def resolve_scalar(
498
527
  option: The option to resolve.
499
528
  toml_data: Parsed `config.toml` mapping (see `load_config_toml`).
500
529
 
530
+ Resolution order is: the prefixed primary `env_var`, then each
531
+ `fallback_env_vars` name in declaration order, then `config.toml`, then the
532
+ typed `default`.
533
+
501
534
  Returns:
502
535
  `(value, source)`, where `source` is `env (<name>)`, `config.toml`, or
503
536
  `default`. A malformed `int`/`float`/list/PTC value, an unrecognized
504
537
  boolean token, or any TOML value of the wrong type is logged and skipped
505
538
  so the next layer (or the typed default) applies. An empty env value is
506
539
  treated as unset (mirroring `resolve_env_var`), so it falls through to
507
- `config.toml`/`default` rather than counting as set. Theme resolution
508
- (`THEME_DELEGATE`) reports its own richer `config.toml [ui.*]` sources.
540
+ the next env var, then `config.toml`/`default`, rather than counting as
541
+ set. Theme resolution (`THEME_DELEGATE`) reports its own richer
542
+ `config.toml [ui.*]` sources.
509
543
  """
510
544
  if option.kind is OptionKind.THEME_DELEGATE:
511
545
  return _resolve_theme(toml_data)
512
546
 
513
- if option.env_var:
547
+ if option.env_var or option.fallback_env_vars:
514
548
  from deepagents_code.model_config import resolved_env_var_name
515
549
 
516
- name = resolved_env_var_name(option.env_var)
517
- # An empty string counts as unset, matching `resolve_env_var`: this
518
- # keeps `config show`/`get` aligned with what the runtime reads (and
519
- # lets a prefixed empty var suppress a canonical one).
520
- raw = os.environ.get(name)
521
- if raw:
550
+ names: list[str] = []
551
+ if option.env_var:
552
+ names.append(resolved_env_var_name(option.env_var))
553
+ names.extend(option.fallback_env_vars)
554
+ # An empty string counts as unset, matching `resolve_env_var`, so it is
555
+ # skipped and the loop continues to the next name. This keeps
556
+ # `config show`/`get` aligned with what the runtime reads: e.g. an empty
557
+ # prefixed `DEEPAGENTS_CODE_LANGSMITH_PROJECT` falls through to a bare
558
+ # `LANGSMITH_PROJECT`, mirroring `get_langsmith_project_name`. Names are
559
+ # tried in order, so the primary `env_var` wins over any fallback.
560
+ for name in names:
561
+ raw = os.environ.get(name)
562
+ if not raw:
563
+ continue
522
564
  value = _coerce_env(option, raw, name)
523
565
  if value is not _INVALID:
524
566
  return value, f"env ({name})"
@@ -819,7 +861,9 @@ _STATIC_OPTIONS: tuple[ConfigOption, ...] = (
819
861
  group="Models",
820
862
  summary="LangSmith project name for deepagents agent traces.",
821
863
  kind=OptionKind.STR,
864
+ default=LANGSMITH_PROJECT_DEFAULT,
822
865
  env_var=_env_vars.LANGSMITH_PROJECT,
866
+ fallback_env_vars=("LANGSMITH_PROJECT",),
823
867
  settings_field="deepagents_langchain_project",
824
868
  ),
825
869
  ConfigOption(
@@ -704,13 +704,15 @@ def build_missing_tool_notification(tool: str) -> "PendingNotification":
704
704
  key="dep:tavily",
705
705
  title="Web search disabled",
706
706
  body=(
707
- "TAVILY_API_KEY is not set, so web search is disabled.\n\n"
708
- "Get a key at https://tavily.com"
707
+ "No Tavily API key is set, so web search is disabled.\n\n"
708
+ "Enter a key to store it (takes effect next launch), or get one "
709
+ "at https://tavily.com"
709
710
  ),
710
711
  actions=(
711
712
  NotificationAction(
712
- ActionId.OPEN_WEBSITE, "Open tavily.com", primary=True
713
+ ActionId.ENTER_API_KEY, "Enter API key", primary=True
713
714
  ),
715
+ NotificationAction(ActionId.OPEN_WEBSITE, "Open tavily.com"),
714
716
  suppress_action,
715
717
  ),
716
718
  payload=MissingDepPayload(tool="tavily", url="https://tavily.com"),