deepagents-code 0.1.10__tar.gz → 0.1.11__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 (257) hide show
  1. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/CHANGELOG.md +8 -0
  2. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/PKG-INFO +1 -1
  3. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_env_vars.py +10 -0
  4. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_version.py +1 -1
  5. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/app.py +95 -86
  6. deepagents_code-0.1.11/deepagents_code/auth_display.py +137 -0
  7. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/main.py +125 -1
  8. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/update_check.py +43 -0
  9. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/auth.py +2 -16
  10. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/loading.py +23 -4
  11. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/model_selector.py +3 -20
  12. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/pyproject.toml +1 -1
  13. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/conftest.py +14 -0
  14. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_app.py +26 -0
  15. deepagents_code-0.1.11/tests/unit_tests/test_auth_display.py +187 -0
  16. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_loading.py +78 -0
  17. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_main.py +292 -0
  18. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_update_check.py +46 -0
  19. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/uv.lock +47 -47
  20. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/.gitignore +0 -0
  21. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/AGENTS.md +0 -0
  22. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/COMMANDS.md +0 -0
  23. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/DEV.md +0 -0
  24. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/Makefile +0 -0
  25. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/README.md +0 -0
  26. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/THREAT_MODEL.md +0 -0
  27. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/__init__.py +0 -0
  28. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/__main__.py +0 -0
  29. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_ask_user_types.py +0 -0
  30. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_cli_context.py +0 -0
  31. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_constants.py +0 -0
  32. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_debug.py +0 -0
  33. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_git.py +0 -0
  34. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_server_config.py +0 -0
  35. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_session_stats.py +0 -0
  36. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_startup_error.py +0 -0
  37. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_testing_models.py +0 -0
  38. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_textual_patches.py +0 -0
  39. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/agent.py +0 -0
  40. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/app.tcss +0 -0
  41. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/ask_user.py +0 -0
  42. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/auth_store.py +0 -0
  43. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/__init__.py +0 -0
  44. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
  45. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
  46. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  47. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  48. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/clipboard.py +0 -0
  49. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/command_registry.py +0 -0
  50. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/config.py +0 -0
  51. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/configurable_model.py +0 -0
  52. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/default_agent_prompt.md +0 -0
  53. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/editor.py +0 -0
  54. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/event_bus.py +0 -0
  55. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/extras_info.py +0 -0
  56. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/file_ops.py +0 -0
  57. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/filesystem_empty_result.py +0 -0
  58. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/formatting.py +0 -0
  59. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/hooks.py +0 -0
  60. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/input.py +0 -0
  61. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/integrations/__init__.py +0 -0
  62. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/integrations/sandbox_factory.py +0 -0
  63. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/integrations/sandbox_provider.py +0 -0
  64. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/iterm_cursor_guide.py +0 -0
  65. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/local_context.py +0 -0
  66. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_auth.py +0 -0
  67. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_commands.py +0 -0
  68. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_disabled.py +0 -0
  69. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_login_service.py +0 -0
  70. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_oauth_ui.py +0 -0
  71. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/__init__.py +0 -0
  72. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/_registry.py +0 -0
  73. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/base.py +0 -0
  74. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/github.py +0 -0
  75. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/slack.py +0 -0
  76. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_tools.py +0 -0
  77. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_trust.py +0 -0
  78. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/media_utils.py +0 -0
  79. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/model_config.py +0 -0
  80. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/non_interactive.py +0 -0
  81. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/notifications.py +0 -0
  82. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/offload.py +0 -0
  83. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/onboarding.py +0 -0
  84. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/output.py +0 -0
  85. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/project_utils.py +0 -0
  86. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/py.typed +0 -0
  87. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/remote_client.py +0 -0
  88. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/resume_state.py +0 -0
  89. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/server.py +0 -0
  90. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/server_graph.py +0 -0
  91. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/server_manager.py +0 -0
  92. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/sessions.py +0 -0
  93. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/skills/__init__.py +0 -0
  94. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/skills/commands.py +0 -0
  95. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/skills/invocation.py +0 -0
  96. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/skills/load.py +0 -0
  97. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/state_migration.py +0 -0
  98. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/subagents.py +0 -0
  99. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/system_prompt.md +0 -0
  100. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/terminal_capabilities.py +0 -0
  101. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/terminal_escape.py +0 -0
  102. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/textual_adapter.py +0 -0
  103. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/theme.py +0 -0
  104. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/tool_display.py +0 -0
  105. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/tools.py +0 -0
  106. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/ui.py +0 -0
  107. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/unicode_security.py +0 -0
  108. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/__init__.py +0 -0
  109. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/_links.py +0 -0
  110. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/agent_selector.py +0 -0
  111. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/approval.py +0 -0
  112. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/ask_user.py +0 -0
  113. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/autocomplete.py +0 -0
  114. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/chat_input.py +0 -0
  115. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/diff.py +0 -0
  116. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/history.py +0 -0
  117. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/launch_init.py +0 -0
  118. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/mcp_login.py +0 -0
  119. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/mcp_reconnect.py +0 -0
  120. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/mcp_viewer.py +0 -0
  121. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/message_store.py +0 -0
  122. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/messages.py +0 -0
  123. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/notification_center.py +0 -0
  124. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/notification_detail.py +0 -0
  125. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/notification_settings.py +0 -0
  126. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/status.py +0 -0
  127. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/theme_selector.py +0 -0
  128. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/thread_selector.py +0 -0
  129. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/tool_renderers.py +0 -0
  130. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/tool_widgets.py +0 -0
  131. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/update_available.py +0 -0
  132. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/update_progress.py +0 -0
  133. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/welcome.py +0 -0
  134. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/arxiv-search/SKILL.md +0 -0
  135. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  136. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/langgraph-docs/SKILL.md +0 -0
  137. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/skill-creator/SKILL.md +0 -0
  138. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  139. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  140. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/web-research/SKILL.md +0 -0
  141. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/images/tui.png +0 -0
  142. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/scripts/check_imports.py +0 -0
  143. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/scripts/debug_server.sh +0 -0
  144. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/scripts/generate_commands_catalog.py +0 -0
  145. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/scripts/install.sh +0 -0
  146. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/README.md +0 -0
  147. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/__init__.py +0 -0
  148. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/benchmarks/__init__.py +0 -0
  149. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  150. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  151. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/conftest.py +0 -0
  152. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/test_acp_mode.py +0 -0
  153. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/test_compact_resume.py +0 -0
  154. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/test_sandbox_factory.py +0 -0
  155. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/test_sandbox_operations.py +0 -0
  156. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/__init__.py +0 -0
  157. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/skills/__init__.py +0 -0
  158. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/skills/test_commands.py +0 -0
  159. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/skills/test_load.py +0 -0
  160. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/skills/test_skills_json.py +0 -0
  161. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_agent.py +0 -0
  162. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_agent_friendly.py +0 -0
  163. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_agent_selector.py +0 -0
  164. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_approval.py +0 -0
  165. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_args.py +0 -0
  166. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_ask_user.py +0 -0
  167. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  168. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_auth_store.py +0 -0
  169. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_auth_widgets.py +0 -0
  170. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_autocomplete.py +0 -0
  171. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_charset.py +0 -0
  172. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_chat_input.py +0 -0
  173. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_clipboard.py +0 -0
  174. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_command_registry.py +0 -0
  175. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_compact_tool.py +0 -0
  176. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_config.py +0 -0
  177. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_configurable_model.py +0 -0
  178. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_cursor_blink.py +0 -0
  179. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_debug.py +0 -0
  180. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_editor.py +0 -0
  181. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_end_to_end.py +0 -0
  182. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_env_vars.py +0 -0
  183. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_event_bus.py +0 -0
  184. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_exception_handling.py +0 -0
  185. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_extras_info.py +0 -0
  186. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_file_ops.py +0 -0
  187. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_filesystem_empty_result.py +0 -0
  188. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_formatting.py +0 -0
  189. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_git.py +0 -0
  190. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_history.py +0 -0
  191. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_hooks.py +0 -0
  192. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_imports.py +0 -0
  193. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_input_parsing.py +0 -0
  194. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_install_command.py +0 -0
  195. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
  196. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_launch_init.py +0 -0
  197. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_links.py +0 -0
  198. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_local_context.py +0 -0
  199. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_main_acp_mode.py +0 -0
  200. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_main_args.py +0 -0
  201. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_auth.py +0 -0
  202. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_commands.py +0 -0
  203. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_disabled.py +0 -0
  204. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_login_modal.py +0 -0
  205. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_login_service.py +0 -0
  206. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
  207. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_reconnect.py +0 -0
  208. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_tools.py +0 -0
  209. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_trust.py +0 -0
  210. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_viewer.py +0 -0
  211. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_media_utils.py +0 -0
  212. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_message_store.py +0 -0
  213. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_messages.py +0 -0
  214. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_model_config.py +0 -0
  215. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_model_selector.py +0 -0
  216. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_model_switch.py +0 -0
  217. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_non_interactive.py +0 -0
  218. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_notification_center.py +0 -0
  219. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_notification_detail.py +0 -0
  220. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_notifications.py +0 -0
  221. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_offload.py +0 -0
  222. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  223. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_onboarding.py +0 -0
  224. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_output.py +0 -0
  225. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_reload.py +0 -0
  226. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_remote_client.py +0 -0
  227. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_resume_state.py +0 -0
  228. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_sandbox_factory.py +0 -0
  229. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server.py +0 -0
  230. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server_config.py +0 -0
  231. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server_graph.py +0 -0
  232. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server_helpers.py +0 -0
  233. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server_manager.py +0 -0
  234. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_session_stats.py +0 -0
  235. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_sessions.py +0 -0
  236. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_shell_allow_list.py +0 -0
  237. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_skill_invocation.py +0 -0
  238. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_startup_fast_paths.py +0 -0
  239. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_state_migration.py +0 -0
  240. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_status.py +0 -0
  241. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_subagents.py +0 -0
  242. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  243. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_terminal_escape.py +0 -0
  244. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_terminal_progress_preference.py +0 -0
  245. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_textual_adapter.py +0 -0
  246. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_textual_patches.py +0 -0
  247. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_theme.py +0 -0
  248. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_thread_selector.py +0 -0
  249. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_tool_display.py +0 -0
  250. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_ui.py +0 -0
  251. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_unicode_security.py +0 -0
  252. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_update_available.py +0 -0
  253. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_update_progress.py +0 -0
  254. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_version.py +0 -0
  255. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_welcome.py +0 -0
  256. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/tools/__init__.py +0 -0
  257. {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/tools/test_fetch_url.py +0 -0
@@ -2,6 +2,14 @@
2
2
 
3
3
  # Deep Agents Code Changelog
4
4
 
5
+ ## [0.1.11](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.10...deepagents-code==0.1.11) (2026-06-07)
6
+
7
+ ### Bug Fixes
8
+
9
+ * Pause loading timer during approvals ([#3782](https://github.com/langchain-ai/deepagents/issues/3782)) ([f98fb0c](https://github.com/langchain-ai/deepagents/commit/f98fb0c80d08e408a018ea33a8aa7144180f4e93))
10
+ * Run auto-update before startup ([#3784](https://github.com/langchain-ai/deepagents/issues/3784)) ([c160ea3](https://github.com/langchain-ai/deepagents/commit/c160ea3eeda1d0ba707bb524cfd0ce087a854e08))
11
+ * Skip update prompts for editable installs ([#3781](https://github.com/langchain-ai/deepagents/issues/3781)) ([ae2874e](https://github.com/langchain-ai/deepagents/commit/ae2874e8ece96c04233c1a88a9da1bd7b9ee2bb2))
12
+
5
13
  ## [0.1.10](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.9...deepagents-code==0.1.10) (2026-06-05)
6
14
 
7
15
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents-code
3
- Version: 0.1.10
3
+ Version: 0.1.11
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/
@@ -134,6 +134,16 @@ and `/api/show`. See `_ollama_discovery_enabled` for accepted truthy/falsy
134
134
  values.
135
135
  """
136
136
 
137
+ RESTARTED_AFTER_UPDATE = "DEEPAGENTS_CODE_RESTARTED_AFTER_UPDATE"
138
+ """Internal sentinel recording the target version immediately before the
139
+ startup auto-update re-execs the process.
140
+
141
+ Not user-facing. The re-exec'd process consumes it and, if that same version
142
+ still reports as available (a no-op upgrade that did not change the running
143
+ version), skips auto-updating to break out of an otherwise endless
144
+ upgrade/restart loop. Set and read internally across `os.execv`.
145
+ """
146
+
137
147
  SERVER_ENV_PREFIX = "DEEPAGENTS_CODE_SERVER_"
138
148
  """Environment variable prefix used to pass CLI config to the server subprocess."""
139
149
 
@@ -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.10" # x-release-please-version
5
+ __version__ = "0.1.11" # 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."""
@@ -3201,23 +3201,31 @@ class DeepAgentsApp(App):
3201
3201
  self._update_check_done.set()
3202
3202
 
3203
3203
  async def _check_for_updates_impl(self, *, periodic: bool = False) -> None:
3204
- """Check PyPI for a newer version and either auto-update or queue a modal.
3204
+ """Check PyPI for a newer version and surface it in-session.
3205
3205
 
3206
3206
  Phase 1 contacts PyPI and records the latest version on the app.
3207
- Phase 2 either performs the auto-upgrade (when enabled), or
3208
- registers the actionable notice and schedules the update modal.
3207
+ Phase 2 surfaces a detected update without installing it in-session
3208
+ (the actual install runs at startup via `_run_startup_auto_update`):
3209
+ when auto-update is enabled it toasts a prompt to restart so the
3210
+ startup path can upgrade; otherwise it raises an actionable notice
3211
+ (periodic recheck) or registers the notice and schedules the update
3212
+ modal (initial check).
3209
3213
  Phase 2 sets `_update_modal_pending` *only* when the modal is
3210
3214
  actually being scheduled; a detected-but-throttled update
3211
3215
  leaves the event clear so missing-dep toasts still fire.
3212
3216
  """
3213
3217
  # Phase 1: version check (benign failure)
3214
3218
  try:
3219
+ from deepagents_code.config import _is_editable_install
3215
3220
  from deepagents_code.update_check import (
3216
3221
  is_auto_update_enabled,
3217
3222
  is_update_available,
3218
3223
  upgrade_command,
3219
3224
  )
3220
3225
 
3226
+ if await asyncio.to_thread(_is_editable_install):
3227
+ return
3228
+
3221
3229
  available, latest = await asyncio.to_thread(
3222
3230
  is_update_available,
3223
3231
  bypass_cache=periodic,
@@ -3233,68 +3241,16 @@ class DeepAgentsApp(App):
3233
3241
  # Phase 2: auto-update or register actionable notice
3234
3242
  try:
3235
3243
  from deepagents_code._version import __version__ as cli_version
3244
+ from deepagents_code.update_check import (
3245
+ format_installed_age_suffix,
3246
+ format_release_age_parenthetical,
3247
+ mark_update_notified,
3248
+ should_notify_update,
3249
+ )
3236
3250
 
3237
3251
  if is_auto_update_enabled():
3238
- from deepagents_code._env_vars import DEBUG_UPDATE
3239
- from deepagents_code.update_check import (
3240
- create_update_log_path,
3241
- perform_upgrade,
3242
- )
3243
-
3244
- if os.environ.get(DEBUG_UPDATE):
3245
- self.notify(
3246
- "Skipped update install (debug mode).",
3247
- severity="information",
3248
- timeout=4,
3249
- markup=False,
3250
- )
3251
- return
3252
-
3253
- log_path = create_update_log_path()
3254
- self.notify(
3255
- f"Updating to v{latest}... Logs: {log_path}",
3256
- severity="information",
3257
- timeout=5,
3258
- markup=False,
3259
- )
3260
- success, output = await perform_upgrade(log_path=log_path)
3261
- if success:
3262
- self.notify(
3263
- f"Updated to v{latest}. Restart to use the new version.",
3264
- severity="information",
3265
- timeout=10,
3266
- )
3267
- else:
3268
- logger.warning(
3269
- "Background auto-upgrade to v%s failed. Output:\n%s",
3270
- latest,
3271
- output,
3272
- )
3273
- cmd = upgrade_command()
3274
- snippet = _truncate(output, limit=160) if output else ""
3275
- message = (
3276
- f"Auto-update failed. Run manually: {cmd}\nLog: {log_path}"
3277
- )
3278
- if snippet:
3279
- message = f"{message}\n{snippet}"
3280
- self.notify(
3281
- message,
3282
- severity="warning",
3283
- timeout=15,
3284
- markup=False,
3285
- )
3286
- else:
3287
- from deepagents_code.update_check import (
3288
- format_installed_age_suffix,
3289
- format_release_age_parenthetical,
3290
- mark_update_notified,
3291
- should_notify_update,
3292
- )
3293
-
3294
3252
  if not await asyncio.to_thread(should_notify_update, latest):
3295
3253
  return
3296
-
3297
- cmd = upgrade_command()
3298
3254
  release_age = await asyncio.to_thread(
3299
3255
  format_release_age_parenthetical,
3300
3256
  latest,
@@ -3303,33 +3259,56 @@ class DeepAgentsApp(App):
3303
3259
  format_installed_age_suffix,
3304
3260
  cli_version,
3305
3261
  )
3306
- notification = self._build_update_notification(
3307
- latest=latest,
3308
- cli_version=cli_version,
3309
- release_age=release_age,
3310
- installed_age=installed_age,
3311
- upgrade_cmd=cmd,
3262
+ self.notify(
3263
+ f"Update available: v{latest}{release_age}. "
3264
+ f"Currently installed: {cli_version}{installed_age}. "
3265
+ "Restart dcode to auto-update before startup.",
3266
+ severity="information",
3267
+ timeout=12,
3268
+ markup=False,
3312
3269
  )
3313
- if periodic:
3314
- self._notify_actionable(
3315
- notification,
3316
- severity="information",
3317
- timeout=12,
3318
- action_hint="Press ctrl+n to install.",
3319
- )
3320
- await asyncio.to_thread(mark_update_notified, latest)
3321
- return
3322
- # Register without a toast: the dedicated modal is
3323
- # the update's UI, so a parallel toast would be
3324
- # redundant. Registration still makes the entry
3325
- # reachable via ctrl+n if the modal is dismissed.
3326
- self._notice_registry.add(notification)
3327
3270
  await asyncio.to_thread(mark_update_notified, latest)
3328
- # Set *before* scheduling the modal: the optional-tools
3329
- # worker may race with this path, and it gates toast
3330
- # suppression on this event.
3331
- self._update_modal_pending.set()
3332
- self.call_after_refresh(self._open_update_available_modal, notification)
3271
+ return
3272
+
3273
+ if not await asyncio.to_thread(should_notify_update, latest):
3274
+ return
3275
+
3276
+ cmd = upgrade_command()
3277
+ release_age = await asyncio.to_thread(
3278
+ format_release_age_parenthetical,
3279
+ latest,
3280
+ )
3281
+ installed_age = await asyncio.to_thread(
3282
+ format_installed_age_suffix,
3283
+ cli_version,
3284
+ )
3285
+ notification = self._build_update_notification(
3286
+ latest=latest,
3287
+ cli_version=cli_version,
3288
+ release_age=release_age,
3289
+ installed_age=installed_age,
3290
+ upgrade_cmd=cmd,
3291
+ )
3292
+ if periodic:
3293
+ self._notify_actionable(
3294
+ notification,
3295
+ severity="information",
3296
+ timeout=12,
3297
+ action_hint="Press ctrl+n to install.",
3298
+ )
3299
+ await asyncio.to_thread(mark_update_notified, latest)
3300
+ return
3301
+ # Register without a toast: the dedicated modal is
3302
+ # the update's UI, so a parallel toast would be
3303
+ # redundant. Registration still makes the entry
3304
+ # reachable via ctrl+n if the modal is dismissed.
3305
+ self._notice_registry.add(notification)
3306
+ await asyncio.to_thread(mark_update_notified, latest)
3307
+ # Set *before* scheduling the modal: the optional-tools
3308
+ # worker may race with this path, and it gates toast
3309
+ # suppression on this event.
3310
+ self._update_modal_pending.set()
3311
+ self.call_after_refresh(self._open_update_available_modal, notification)
3333
3312
  except Exception:
3334
3313
  logger.warning("Update check/notify failed unexpectedly", exc_info=True)
3335
3314
  if is_auto_update_enabled():
@@ -4101,6 +4080,24 @@ class DeepAgentsApp(App):
4101
4080
  # Cosmetic only: must never break app startup or theme changes.
4102
4081
  logger.warning("set_terminal_background raised unexpectedly", exc_info=True)
4103
4082
 
4083
+ def _pause_loading_spinner_for_approval(self) -> None:
4084
+ """Pause the global spinner timer while an approval widget is visible."""
4085
+ if self._loading_widget is not None:
4086
+ self._loading_widget.pause()
4087
+
4088
+ def _resume_loading_spinner_after_approval(
4089
+ self,
4090
+ _future: asyncio.Future[Any] | None = None,
4091
+ ) -> None:
4092
+ """Resume the global spinner timer after an approval decision.
4093
+
4094
+ Accepts an unused `_future` argument so it can be registered directly as
4095
+ a `Future.add_done_callback`, which always passes the completed future
4096
+ positionally.
4097
+ """
4098
+ if self._loading_widget is not None:
4099
+ self._loading_widget.resume()
4100
+
4104
4101
  async def _set_spinner(self, status: SpinnerStatus) -> None:
4105
4102
  """Show, update, or hide the loading spinner.
4106
4103
 
@@ -4149,6 +4146,11 @@ class DeepAgentsApp(App):
4149
4146
  self._loading_widget = LoadingWidget(status)
4150
4147
  await self._mount_before_queued(messages, self._loading_widget)
4151
4148
  else:
4149
+ # A fresh status update means the agent is active again, so
4150
+ # un-pause as a backstop in case an approval future was ever
4151
+ # abandoned without completing the resume callback. `resume()` is a
4152
+ # no-op when the spinner is not paused.
4153
+ self._loading_widget.resume()
4152
4154
  # Update existing
4153
4155
  self._loading_widget.set_status(status)
4154
4156
  # Reposition via move_child so elapsed-time and animation state
@@ -4261,6 +4263,13 @@ class DeepAgentsApp(App):
4261
4263
  while self._pending_approval_widget is not None: # noqa: ASYNC110 # Simple polling is sufficient here
4262
4264
  await asyncio.sleep(0.1)
4263
4265
 
4266
+ # Pause the elapsed-time counter while the user decides, then resume it
4267
+ # when the decision future completes. Resolve, reject, and cancel all
4268
+ # fire the done-callback; the `_set_spinner` backstop covers the
4269
+ # remaining case where a future is abandoned without completing.
4270
+ self._pause_loading_spinner_for_approval()
4271
+ result_future.add_done_callback(self._resume_loading_spinner_after_approval)
4272
+
4264
4273
  # Create menu with unique ID to avoid conflicts
4265
4274
  from deepagents_code.widgets.approval import ApprovalMenu
4266
4275
 
@@ -0,0 +1,137 @@
1
+ """Shared provider auth status formatting."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, assert_never
6
+
7
+ from textual.content import Content
8
+
9
+ from deepagents_code.model_config import (
10
+ ProviderAuthSource,
11
+ ProviderAuthState,
12
+ ProviderAuthStatus,
13
+ resolved_env_var_name,
14
+ )
15
+
16
+ if TYPE_CHECKING:
17
+ from deepagents_code.config import Glyphs
18
+
19
+
20
+ def format_auth_badge(status: ProviderAuthStatus) -> Content:
21
+ """Format an auth manager badge for a provider.
22
+
23
+ Used by the `/auth` manager, where each provider renders a bracketed,
24
+ styled badge (e.g. `[stored]`, `[env: ANTHROPIC_API_KEY]`, `[missing]`).
25
+
26
+ Args:
27
+ status: Provider auth/readiness status.
28
+
29
+ Returns:
30
+ A styled badge `Content` for the auth manager surface.
31
+ """
32
+ state = status.state
33
+ match state:
34
+ case ProviderAuthState.CONFIGURED:
35
+ return _format_configured_badge(status)
36
+ case ProviderAuthState.MISSING:
37
+ return Content.styled("[missing]", "bold $warning")
38
+ case ProviderAuthState.NOT_REQUIRED:
39
+ return _auth_badge(status.detail or "no API key required")
40
+ case ProviderAuthState.IMPLICIT:
41
+ return _auth_badge(status.detail or "implicit auth")
42
+ case ProviderAuthState.MANAGED:
43
+ return _auth_badge(status.detail or "custom auth")
44
+ case ProviderAuthState.UNKNOWN:
45
+ return _auth_badge(status.detail or "credentials unknown", prefix="? ")
46
+ case _:
47
+ assert_never(state)
48
+
49
+
50
+ def format_auth_indicator(status: ProviderAuthStatus, glyphs: Glyphs) -> str:
51
+ """Format a model selector provider-header indicator.
52
+
53
+ Used by `/model`, where the indicator is plain text shown next to the
54
+ provider name. Returns an empty string for `CONFIGURED` providers, which
55
+ need no indicator.
56
+
57
+ Args:
58
+ status: Provider auth/readiness status.
59
+ glyphs: Glyph table for the active terminal mode.
60
+
61
+ Returns:
62
+ Text shown next to the provider name, or an empty string when no
63
+ indicator should be rendered (e.g., `CONFIGURED`).
64
+ """
65
+ state = status.state
66
+ match state:
67
+ case ProviderAuthState.CONFIGURED:
68
+ return ""
69
+ case ProviderAuthState.MISSING:
70
+ if status.env_var:
71
+ return f"{glyphs.warning} missing {status.env_var}"
72
+ return f"{glyphs.warning} missing credentials"
73
+ case ProviderAuthState.NOT_REQUIRED:
74
+ return status.detail or "no API key required"
75
+ case ProviderAuthState.IMPLICIT:
76
+ return status.detail or "implicit auth"
77
+ case ProviderAuthState.MANAGED:
78
+ return status.detail or "custom auth"
79
+ case ProviderAuthState.UNKNOWN:
80
+ detail = status.detail or "credentials unknown"
81
+ return f"{glyphs.question} {detail}"
82
+ case _:
83
+ assert_never(state)
84
+
85
+
86
+ def _auth_badge(detail: str, *, prefix: str = "") -> Content:
87
+ """Format a muted auth manager badge.
88
+
89
+ Args:
90
+ detail: Badge text inside the brackets.
91
+ prefix: Text prepended verbatim before `detail` inside the brackets.
92
+ Callers include any separator themselves (e.g. `"? "`); this
93
+ helper does not insert one.
94
+
95
+ Returns:
96
+ Formatted auth manager badge content.
97
+ """
98
+ return Content.assemble(
99
+ ("[", "$text-muted"),
100
+ (prefix, "$text-muted"),
101
+ Content.styled(detail, "$text-muted"),
102
+ ("]", "$text-muted"),
103
+ )
104
+
105
+
106
+ def _format_configured_badge(status: ProviderAuthStatus) -> Content:
107
+ """Format the auth manager badge for a `CONFIGURED` provider.
108
+
109
+ Args:
110
+ status: A `CONFIGURED` provider auth status.
111
+
112
+ Returns:
113
+ A styled badge naming the credential source (`[stored]` or `[env: …]`).
114
+
115
+ Raises:
116
+ ValueError: If the status carries no source. `ProviderAuthStatus`
117
+ guarantees `CONFIGURED` implies a source, so this guards against
118
+ that invariant being violated rather than a normal input.
119
+ """
120
+ match status.source:
121
+ case ProviderAuthSource.STORED:
122
+ return Content.styled("[stored]", "bold $success")
123
+ case ProviderAuthSource.ENV:
124
+ if status.env_var:
125
+ return Content.assemble(
126
+ ("[env: ", "$text-muted"),
127
+ Content.styled(
128
+ resolved_env_var_name(status.env_var), "$text-muted"
129
+ ),
130
+ ("]", "$text-muted"),
131
+ )
132
+ return Content.styled("[env]", "$text-muted")
133
+ case None:
134
+ msg = f"CONFIGURED auth status has no source: {status!r}"
135
+ raise ValueError(msg)
136
+ case _:
137
+ assert_never(status.source)
@@ -20,9 +20,11 @@ import sys
20
20
  import traceback
21
21
  from collections.abc import Callable, Sequence
22
22
  from pathlib import Path
23
- from typing import TYPE_CHECKING, Any
23
+ from typing import TYPE_CHECKING, Any, NoReturn
24
24
 
25
25
  if TYPE_CHECKING:
26
+ from rich.console import Console
27
+
26
28
  from deepagents_code.app import AppResult
27
29
  from deepagents_code.mcp_tools import MCPServerInfo
28
30
  from deepagents_code.notifications import PendingNotification
@@ -35,6 +37,127 @@ from deepagents_code._version import __version__
35
37
  logger = logging.getLogger(__name__)
36
38
 
37
39
 
40
+ def _restart_current_process() -> NoReturn:
41
+ """Replace the current process with a fresh `deepagents_code` invocation.
42
+
43
+ Raises:
44
+ RuntimeError: If process replacement unexpectedly returns.
45
+ """
46
+ argv = [sys.executable, "-m", "deepagents_code", *sys.argv[1:]]
47
+ # Re-exec the trusted interpreter with the user's own argv verbatim; the
48
+ # only "input" is the command the user already ran, so S606's concern
49
+ # (untrusted/unsanitized args to a spawned executable) does not apply.
50
+ os.execv(sys.executable, argv) # noqa: S606
51
+ msg = "os.execv returned unexpectedly"
52
+ raise RuntimeError(msg)
53
+
54
+
55
+ def _run_startup_auto_update(console: "Console") -> None:
56
+ """Apply enabled auto-updates before the TUI and server start.
57
+
58
+ On a successful upgrade the process is re-exec'd so the new version is
59
+ loaded. Any failure is fail-soft: the installed version is launched and
60
+ the error is surfaced, never blocking startup.
61
+
62
+ Raises:
63
+ SystemExit: Re-raised rather than suppressed by the fail-soft handler,
64
+ so a process-exit request is never swallowed (the `os.execv`
65
+ re-exec is simulated this way under test).
66
+ """
67
+ from rich.markup import escape
68
+
69
+ from deepagents_code._env_vars import DEBUG_UPDATE, RESTARTED_AFTER_UPDATE
70
+ from deepagents_code._version import __version__ as cli_version
71
+ from deepagents_code.config import _is_editable_install
72
+ from deepagents_code.update_check import (
73
+ create_update_log_path,
74
+ format_release_age_parenthetical,
75
+ get_cached_update_available,
76
+ is_auto_update_enabled,
77
+ perform_upgrade,
78
+ upgrade_command,
79
+ )
80
+
81
+ try:
82
+ if _is_editable_install() or not is_auto_update_enabled():
83
+ return
84
+ # Consume the re-exec sentinel recorded before the previous restart.
85
+ restarted_for = os.environ.pop(RESTARTED_AFTER_UPDATE, None)
86
+ available, latest = get_cached_update_available()
87
+ if not available or latest is None:
88
+ return
89
+ if restarted_for == latest:
90
+ # Already restarted after upgrading to this version, yet it still
91
+ # reports as available: the install did not change the running
92
+ # version. Bail out instead of upgrading and restarting forever
93
+ # (this runs before the TUI, so there is no in-app way to stop it).
94
+ cmd = upgrade_command()
95
+ console.print(
96
+ f"[bold yellow]Warning:[/bold yellow] v{latest} still reports as "
97
+ "available after an automatic update; skipping auto-update to "
98
+ f"avoid a restart loop. Update manually: [cyan]{cmd}[/cyan]\n"
99
+ f"Continuing with v{cli_version}.",
100
+ highlight=False,
101
+ )
102
+ return
103
+ release_age = format_release_age_parenthetical(latest)
104
+ console.print(
105
+ f"Auto-updating deepagents-code from v{cli_version} to "
106
+ f"v{latest}{release_age} before startup..."
107
+ )
108
+ if os.environ.get(DEBUG_UPDATE):
109
+ console.print("Skipped update install (debug mode).", style="dim")
110
+ return
111
+ log_path = create_update_log_path()
112
+ console.print(
113
+ f"Update log: {log_path}\nTail progress: tail -f {log_path}",
114
+ style="dim",
115
+ highlight=False,
116
+ markup=False,
117
+ )
118
+ success, output = asyncio.run(perform_upgrade(log_path=log_path))
119
+ if success:
120
+ console.print(f"[green]Updated to v{latest}. Restarting...[/green]")
121
+ # Record the target version so the re-exec'd process can detect a
122
+ # no-op upgrade and break the loop (see the `restarted_for` guard).
123
+ os.environ[RESTARTED_AFTER_UPDATE] = latest
124
+ try:
125
+ _restart_current_process()
126
+ except (OSError, RuntimeError):
127
+ # Upgrade succeeded but the re-exec did not happen (`os.execv`
128
+ # raised, or returned unexpectedly). Drop the sentinel and
129
+ # continue on the old in-memory code; the user must restart
130
+ # manually to load the new version.
131
+ os.environ.pop(RESTARTED_AFTER_UPDATE, None)
132
+ logger.warning("Restart after update failed", exc_info=True)
133
+ console.print(
134
+ f"[bold yellow]Warning:[/bold yellow] Updated to v{latest} but "
135
+ "the automatic restart failed. Restart dcode manually to use "
136
+ "the new version.",
137
+ highlight=False,
138
+ )
139
+ return
140
+ cmd = upgrade_command()
141
+ detail = f": {escape(output[:200])}" if output else ""
142
+ console.print(
143
+ f"[bold red]Auto-update failed{detail}[/bold red]\n"
144
+ f"Run manually: [cyan]{cmd}[/cyan]\n"
145
+ f"Continuing with v{cli_version}.",
146
+ markup=True,
147
+ highlight=False,
148
+ )
149
+ except SystemExit:
150
+ # Process replacement (and test doubles that simulate it) must not be
151
+ # swallowed by the fail-soft handler below.
152
+ raise
153
+ except Exception:
154
+ logger.warning("Startup auto-update failed", exc_info=True)
155
+ console.print(
156
+ "[bold yellow]Warning:[/bold yellow] Auto-update failed before startup; "
157
+ "continuing with the installed version."
158
+ )
159
+
160
+
38
161
  def _resolve_agent_arg(args: argparse.Namespace) -> str:
39
162
  """Resolve the final agent identifier from parsed CLI args.
40
163
 
@@ -2491,6 +2614,7 @@ def cli_main() -> None:
2491
2614
  sys.exit(130)
2492
2615
  sys.exit(exit_code)
2493
2616
  else:
2617
+ _run_startup_auto_update(console)
2494
2618
  # Resolve recent-agent fallback only for actual session launches.
2495
2619
  assistant_id = _resolve_agent_arg(args)
2496
2620
  # Interactive mode - handle thread resume
@@ -158,6 +158,49 @@ def _latest_from_releases(
158
158
  return best_str
159
159
 
160
160
 
161
+ def get_cached_update_available() -> tuple[bool, str | None]:
162
+ """Check for updates using only a fresh local cache entry.
163
+
164
+ This is the startup fast path: it never contacts PyPI. Stale, missing,
165
+ corrupt, or unparsable cache data is treated as "no cached update answer" so
166
+ callers can launch immediately and let a background update check refresh the
167
+ cache later.
168
+
169
+ Returns:
170
+ A `(available, latest)` tuple. `latest` is `None` when the cache cannot
171
+ provide a fresh answer.
172
+ """
173
+ try:
174
+ installed = _parse_version(__version__)
175
+ except InvalidVersion:
176
+ logger.warning(
177
+ "Installed version %r is not PEP 440 compliant; "
178
+ "cache-only update checks disabled for this install",
179
+ __version__,
180
+ )
181
+ return False, None
182
+
183
+ cache_key = "version_prerelease" if installed.is_prerelease else "version"
184
+ try:
185
+ if not CACHE_FILE.exists():
186
+ return False, None
187
+ data = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
188
+ if not isinstance(data, dict):
189
+ return False, None
190
+ checked_at = data.get("checked_at")
191
+ if not isinstance(checked_at, (int, float)):
192
+ return False, None
193
+ if time.time() - checked_at >= CACHE_TTL:
194
+ return False, None
195
+ value = data.get(cache_key)
196
+ if not isinstance(value, str):
197
+ return False, None
198
+ return _parse_version(value) > installed, value
199
+ except (OSError, json.JSONDecodeError, TypeError, InvalidVersion):
200
+ logger.debug("Failed to read cache-only update answer", exc_info=True)
201
+ return False, None
202
+
203
+
161
204
  def get_latest_version(
162
205
  *,
163
206
  bypass_cache: bool = False,
@@ -35,19 +35,18 @@ if TYPE_CHECKING:
35
35
  from textual.events import Click
36
36
 
37
37
  from deepagents_code import auth_store, theme
38
+ from deepagents_code.auth_display import format_auth_badge
38
39
  from deepagents_code.config import get_glyphs, is_ascii_mode
39
40
  from deepagents_code.model_config import (
40
41
  PROVIDER_API_KEY_ENV,
41
42
  PROVIDERS_DOCS_URL as _PROVIDERS_DOCS_URL,
42
43
  ModelConfig,
43
- ProviderAuthSource,
44
44
  clear_caches,
45
45
  get_available_models,
46
46
  get_base_url_env_var,
47
47
  get_credential_env_var,
48
48
  get_default_base_url_env,
49
49
  get_provider_auth_status,
50
- resolved_env_var_name,
51
50
  )
52
51
  from deepagents_code.widgets._links import open_style_link
53
52
 
@@ -671,20 +670,7 @@ class AuthManagerScreen(ModalScreen[None]):
671
670
  A composed `Content` with the provider name and a status badge.
672
671
  """
673
672
  status = get_provider_auth_status(provider)
674
- env_var = status.env_var or get_credential_env_var(provider) or ""
675
- if status.source is ProviderAuthSource.STORED:
676
- badge = Content.styled("[stored]", "bold $success")
677
- elif status.source is ProviderAuthSource.ENV:
678
- if env_var:
679
- badge = Content.assemble(
680
- ("[env: ", "$text-muted"),
681
- Content.styled(resolved_env_var_name(env_var), "$text-muted"),
682
- ("]", "$text-muted"),
683
- )
684
- else:
685
- badge = Content.styled("[env]", "$text-muted")
686
- else:
687
- badge = Content.styled("[missing]", "bold $warning")
673
+ badge = format_auth_badge(status)
688
674
  return Content.assemble(
689
675
  Content.from_markup("$provider", provider=provider),
690
676
  " ",