deepagents-cli 0.0.41__tar.gz → 0.0.42__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 (220) hide show
  1. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/CHANGELOG.md +20 -11
  2. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/PKG-INFO +5 -5
  3. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_env_vars.py +3 -0
  4. deepagents_cli-0.0.42/deepagents_cli/_textual_patches.py +66 -0
  5. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_version.py +1 -1
  6. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/agent.py +34 -7
  7. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/app.py +23 -1
  8. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/config.py +82 -35
  9. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/local_context.py +21 -7
  10. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/system_prompt.md +1 -1
  11. deepagents_cli-0.0.42/deepagents_cli/terminal_capabilities.py +115 -0
  12. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/model_selector.py +23 -2
  13. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/pyproject.toml +7 -4
  14. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/conftest.py +15 -0
  15. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_agent.py +110 -6
  16. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_config.py +372 -45
  17. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_local_context.py +39 -10
  18. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_model_switch.py +80 -0
  19. deepagents_cli-0.0.42/tests/unit_tests/test_terminal_capabilities.py +203 -0
  20. deepagents_cli-0.0.42/tests/unit_tests/test_textual_patches.py +92 -0
  21. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_version.py +11 -1
  22. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/uv.lock +34 -21
  23. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/.gitignore +0 -0
  24. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/DEV.md +0 -0
  25. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/Makefile +0 -0
  26. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/README.md +0 -0
  27. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/THREAT_MODEL.md +0 -0
  28. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/__init__.py +0 -0
  29. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/__main__.py +0 -0
  30. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_ask_user_types.py +0 -0
  31. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_cli_context.py +0 -0
  32. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_debug.py +0 -0
  33. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_git.py +0 -0
  34. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_server_config.py +0 -0
  35. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_session_stats.py +0 -0
  36. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/_testing_models.py +0 -0
  37. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/app.tcss +0 -0
  38. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/ask_user.py +0 -0
  39. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/built_in_skills/__init__.py +0 -0
  40. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/built_in_skills/remember/SKILL.md +0 -0
  41. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/built_in_skills/skill-creator/SKILL.md +0 -0
  42. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  43. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  44. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/clipboard.py +0 -0
  45. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/command_registry.py +0 -0
  46. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/configurable_model.py +0 -0
  47. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/default_agent_prompt.md +0 -0
  48. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/deploy/__init__.py +0 -0
  49. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/deploy/bundler.py +0 -0
  50. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/deploy/commands.py +0 -0
  51. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/deploy/config.py +0 -0
  52. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/deploy/templates.py +0 -0
  53. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/editor.py +0 -0
  54. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/extras_info.py +0 -0
  55. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/file_ops.py +0 -0
  56. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/formatting.py +0 -0
  57. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/hooks.py +0 -0
  58. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/input.py +0 -0
  59. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/integrations/__init__.py +0 -0
  60. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/integrations/sandbox_factory.py +0 -0
  61. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/integrations/sandbox_provider.py +0 -0
  62. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/main.py +0 -0
  63. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/mcp_tools.py +0 -0
  64. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/mcp_trust.py +0 -0
  65. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/media_utils.py +0 -0
  66. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/model_config.py +0 -0
  67. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/non_interactive.py +0 -0
  68. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/notifications.py +0 -0
  69. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/offload.py +0 -0
  70. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/output.py +0 -0
  71. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/project_utils.py +0 -0
  72. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/py.typed +0 -0
  73. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/remote_client.py +0 -0
  74. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/server.py +0 -0
  75. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/server_graph.py +0 -0
  76. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/server_manager.py +0 -0
  77. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/sessions.py +0 -0
  78. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/skills/__init__.py +0 -0
  79. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/skills/commands.py +0 -0
  80. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/skills/invocation.py +0 -0
  81. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/skills/load.py +0 -0
  82. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/subagents.py +0 -0
  83. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/textual_adapter.py +0 -0
  84. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/theme.py +0 -0
  85. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/token_state.py +0 -0
  86. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/tool_display.py +0 -0
  87. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/tools.py +0 -0
  88. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/ui.py +0 -0
  89. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/unicode_security.py +0 -0
  90. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/update_check.py +0 -0
  91. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/__init__.py +0 -0
  92. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/_links.py +0 -0
  93. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/agent_selector.py +0 -0
  94. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/approval.py +0 -0
  95. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/ask_user.py +0 -0
  96. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/autocomplete.py +0 -0
  97. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/chat_input.py +0 -0
  98. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/diff.py +0 -0
  99. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/history.py +0 -0
  100. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/loading.py +0 -0
  101. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/mcp_viewer.py +0 -0
  102. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/message_store.py +0 -0
  103. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/messages.py +0 -0
  104. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/notification_center.py +0 -0
  105. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/notification_detail.py +0 -0
  106. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/notification_settings.py +0 -0
  107. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/status.py +0 -0
  108. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/theme_selector.py +0 -0
  109. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/thread_selector.py +0 -0
  110. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/tool_renderers.py +0 -0
  111. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/tool_widgets.py +0 -0
  112. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/update_available.py +0 -0
  113. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/deepagents_cli/widgets/welcome.py +0 -0
  114. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/deploy-content-writer/.env.example +0 -0
  115. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/deploy-content-writer/skills/blog-post/SKILL.md +0 -0
  116. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/deploy-content-writer/skills/social-media/SKILL.md +0 -0
  117. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/deploy-content-writer/user/context.md +0 -0
  118. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/deploy-content-writer/user/preferences.md +0 -0
  119. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/skills/arxiv-search/SKILL.md +0 -0
  120. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  121. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/skills/langgraph-docs/SKILL.md +0 -0
  122. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/skills/skill-creator/SKILL.md +0 -0
  123. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  124. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  125. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/examples/skills/web-research/SKILL.md +0 -0
  126. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/images/cli.png +0 -0
  127. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/scripts/check_imports.py +0 -0
  128. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/scripts/debug_server.sh +0 -0
  129. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/scripts/install.sh +0 -0
  130. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/README.md +0 -0
  131. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/__init__.py +0 -0
  132. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/benchmarks/__init__.py +0 -0
  133. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  134. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  135. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/conftest.py +0 -0
  136. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/test_acp_mode.py +0 -0
  137. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/test_compact_resume.py +0 -0
  138. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/test_sandbox_factory.py +0 -0
  139. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/integration_tests/test_sandbox_operations.py +0 -0
  140. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/__init__.py +0 -0
  141. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/deploy/__init__.py +0 -0
  142. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/deploy/test_bundler.py +0 -0
  143. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/deploy/test_commands.py +0 -0
  144. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/deploy/test_config.py +0 -0
  145. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/skills/__init__.py +0 -0
  146. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/skills/test_commands.py +0 -0
  147. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/skills/test_load.py +0 -0
  148. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/skills/test_skills_json.py +0 -0
  149. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_agent_friendly.py +0 -0
  150. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_agent_selector.py +0 -0
  151. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_app.py +0 -0
  152. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_approval.py +0 -0
  153. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_args.py +0 -0
  154. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_ask_user.py +0 -0
  155. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  156. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_autocomplete.py +0 -0
  157. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_charset.py +0 -0
  158. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_chat_input.py +0 -0
  159. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_command_registry.py +0 -0
  160. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_compact_tool.py +0 -0
  161. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_configurable_model.py +0 -0
  162. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_debug.py +0 -0
  163. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_editor.py +0 -0
  164. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_end_to_end.py +0 -0
  165. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_env_vars.py +0 -0
  166. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_exception_handling.py +0 -0
  167. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_extras_info.py +0 -0
  168. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_file_ops.py +0 -0
  169. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_formatting.py +0 -0
  170. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_git.py +0 -0
  171. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_history.py +0 -0
  172. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_hooks.py +0 -0
  173. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_imports.py +0 -0
  174. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_input_parsing.py +0 -0
  175. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_links.py +0 -0
  176. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_loading.py +0 -0
  177. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_main.py +0 -0
  178. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_main_acp_mode.py +0 -0
  179. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_main_args.py +0 -0
  180. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_mcp_tools.py +0 -0
  181. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_mcp_trust.py +0 -0
  182. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_mcp_viewer.py +0 -0
  183. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_media_utils.py +0 -0
  184. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_message_store.py +0 -0
  185. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_messages.py +0 -0
  186. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_model_config.py +0 -0
  187. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_model_selector.py +0 -0
  188. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_non_interactive.py +0 -0
  189. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_notification_center.py +0 -0
  190. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_notification_detail.py +0 -0
  191. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_notifications.py +0 -0
  192. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_offload.py +0 -0
  193. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  194. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_output.py +0 -0
  195. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_reload.py +0 -0
  196. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_remote_client.py +0 -0
  197. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_sandbox_factory.py +0 -0
  198. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_server.py +0 -0
  199. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_server_config.py +0 -0
  200. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_server_graph.py +0 -0
  201. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_server_helpers.py +0 -0
  202. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_server_manager.py +0 -0
  203. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_session_stats.py +0 -0
  204. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_sessions.py +0 -0
  205. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_shell_allow_list.py +0 -0
  206. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_skill_invocation.py +0 -0
  207. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_status.py +0 -0
  208. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_subagents.py +0 -0
  209. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_textual_adapter.py +0 -0
  210. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_theme.py +0 -0
  211. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_thread_selector.py +0 -0
  212. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_token_tracker.py +0 -0
  213. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_tool_display.py +0 -0
  214. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_ui.py +0 -0
  215. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_unicode_security.py +0 -0
  216. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_update_available.py +0 -0
  217. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_update_check.py +0 -0
  218. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/test_welcome.py +0 -0
  219. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/tools/__init__.py +0 -0
  220. {deepagents_cli-0.0.41 → deepagents_cli-0.0.42}/tests/unit_tests/tools/test_fetch_url.py +0 -0
@@ -1,23 +1,32 @@
1
1
  # Changelog
2
2
 
3
- ## [0.0.41](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.40...deepagents-cli==0.0.41) (2026-04-21)
4
-
3
+ ## [0.0.42](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.41...deepagents-cli==0.0.42) (2026-04-29)
5
4
 
6
5
  ### Features
7
6
 
8
- * **cli:** `--startup-cmd` flag ([#2841](https://github.com/langchain-ai/deepagents/issues/2841)) ([8adcc2c](https://github.com/langchain-ai/deepagents/commit/8adcc2c2e612346c263d02d1ec5c33e0d63da5a3))
9
- * **cli:** actionable notifications, update modal ([#2855](https://github.com/langchain-ai/deepagents/issues/2855)) ([5fcd368](https://github.com/langchain-ai/deepagents/commit/5fcd368088079a84f151e0a3f5e4b9ac29c360c1))
10
- * **cli:** custom auth via `[auth]` ([#2734](https://github.com/langchain-ai/deepagents/issues/2734)) ([417ddaa](https://github.com/langchain-ai/deepagents/commit/417ddaab4804a54a8f83721bb8577d65cfa3659f))
11
- * **cli:** refresh footer git branch after shell commands ([#2851](https://github.com/langchain-ai/deepagents/issues/2851)) ([ee4fddd](https://github.com/langchain-ai/deepagents/commit/ee4fddde9454ee8f7ede98ee9a346da6c5ccd3d9))
12
- * **cli:** rework `/version`, add release-age and editable-install guard ([#2854](https://github.com/langchain-ai/deepagents/issues/2854)) ([1ae053f](https://github.com/langchain-ai/deepagents/commit/1ae053f347679e58562d2b81eb6d6e6e9bbf0b07))
7
+ * `shift+enter` newline on kitty-capable terminals ([#2869](https://github.com/langchain-ai/deepagents/issues/2869)) ([34e6614](https://github.com/langchain-ai/deepagents/commit/34e6614a2c6e6a7e33f763ac5b527c13b324a690))
8
+ * Hint `Enter` behavior in `/model` empty state ([#2933](https://github.com/langchain-ai/deepagents/issues/2933)) ([7cffa16](https://github.com/langchain-ai/deepagents/commit/7cffa16a072bc77def78e4cda09c2e5a2cf8ca39))
9
+
10
+ ### Bug Fixes
11
+
12
+ * Queue `/model` switches during server startup ([#2895](https://github.com/langchain-ai/deepagents/issues/2895)) ([6ed7b65](https://github.com/langchain-ai/deepagents/commit/6ed7b65a40ac5bd926e94edd944bd552e8d6b5b4))
13
+
14
+ ## [0.0.41](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.40...deepagents-cli==0.0.41) (2026-04-21)
15
+
16
+ ### Features
13
17
 
18
+ * `--startup-cmd` flag ([#2841](https://github.com/langchain-ai/deepagents/issues/2841)) ([8adcc2c](https://github.com/langchain-ai/deepagents/commit/8adcc2c2e612346c263d02d1ec5c33e0d63da5a3))
19
+ * Actionable notifications, update modal ([#2855](https://github.com/langchain-ai/deepagents/issues/2855)) ([5fcd368](https://github.com/langchain-ai/deepagents/commit/5fcd368088079a84f151e0a3f5e4b9ac29c360c1))
20
+ * Custom auth via `[auth]` in `deepagents deploy` ([#2734](https://github.com/langchain-ai/deepagents/issues/2734)) ([417ddaa](https://github.com/langchain-ai/deepagents/commit/417ddaab4804a54a8f83721bb8577d65cfa3659f))
21
+ * Refresh footer git branch after shell commands ([#2851](https://github.com/langchain-ai/deepagents/issues/2851)) ([ee4fddd](https://github.com/langchain-ai/deepagents/commit/ee4fddde9454ee8f7ede98ee9a346da6c5ccd3d9))
22
+ * Rework `/version`, add release-age and editable-install guard ([#2854](https://github.com/langchain-ai/deepagents/issues/2854)) ([1ae053f](https://github.com/langchain-ai/deepagents/commit/1ae053f347679e58562d2b81eb6d6e6e9bbf0b07))
14
23
 
15
24
  ### Bug Fixes
16
25
 
17
- * **cli:** gate agent-swap resume hint on server checkpoint ([#2862](https://github.com/langchain-ai/deepagents/issues/2862)) ([f14b9cd](https://github.com/langchain-ai/deepagents/commit/f14b9cd3cc41f92ddb089d4294cba8496d517807))
18
- * **cli:** keep thinking spinner visible during text streaming ([#2849](https://github.com/langchain-ai/deepagents/issues/2849)) ([2cb2244](https://github.com/langchain-ai/deepagents/commit/2cb22446bc1351d20e8bbfa9f23b5d668947351f))
19
- * **cli:** re-show thinking spinner on hitl/`ask_user` resume ([#2847](https://github.com/langchain-ai/deepagents/issues/2847)) ([34a6167](https://github.com/langchain-ai/deepagents/commit/34a6167b0c275c7e4da067ef276a837f60da2f8a))
20
- * **cli:** restore ctrl+j newline keybinding ([#2827](https://github.com/langchain-ai/deepagents/issues/2827)) ([2e1a3f4](https://github.com/langchain-ai/deepagents/commit/2e1a3f45a9ff56390e294ea5b0f6a837200874ec))
26
+ * Gate agent-swap resume hint on server checkpoint ([#2862](https://github.com/langchain-ai/deepagents/issues/2862)) ([f14b9cd](https://github.com/langchain-ai/deepagents/commit/f14b9cd3cc41f92ddb089d4294cba8496d517807))
27
+ * Keep thinking spinner visible during text streaming ([#2849](https://github.com/langchain-ai/deepagents/issues/2849)) ([2cb2244](https://github.com/langchain-ai/deepagents/commit/2cb22446bc1351d20e8bbfa9f23b5d668947351f))
28
+ * Re-show thinking spinner on hitl/`ask_user` resume ([#2847](https://github.com/langchain-ai/deepagents/issues/2847)) ([34a6167](https://github.com/langchain-ai/deepagents/commit/34a6167b0c275c7e4da067ef276a837f60da2f8a))
29
+ * Restore `ctrl+j` newline keybinding ([#2827](https://github.com/langchain-ai/deepagents/issues/2827)) ([2e1a3f4](https://github.com/langchain-ai/deepagents/commit/2e1a3f45a9ff56390e294ea5b0f6a837200874ec))
21
30
 
22
31
  ## [0.0.40](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.39...deepagents-cli==0.0.40) (2026-04-20)
23
32
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents-cli
3
- Version: 0.0.41
3
+ Version: 0.0.42
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/
@@ -27,9 +27,9 @@ Classifier: Topic :: Terminals
27
27
  Requires-Python: <4.0,>=3.11
28
28
  Requires-Dist: aiosqlite<1.0.0,>=0.19.0
29
29
  Requires-Dist: deepagents-acp>=0.0.4
30
- Requires-Dist: deepagents==0.5.3
30
+ Requires-Dist: deepagents==0.5.4
31
31
  Requires-Dist: httpx<1.0.0,>=0.28.1
32
- Requires-Dist: langchain-anthropic<2.0.0,>=1.4.0
32
+ Requires-Dist: langchain-anthropic<2.0.0,>=1.4.2
33
33
  Requires-Dist: langchain-google-genai<5.0.0,>=4.2.1
34
34
  Requires-Dist: langchain-mcp-adapters<1.0.0,>=0.2.0
35
35
  Requires-Dist: langchain-openai<2.0.0,>=1.1.12
@@ -57,7 +57,7 @@ Requires-Dist: uuid-utils<1.0.0,>=0.10.0
57
57
  Provides-Extra: agentcore
58
58
  Requires-Dist: langchain-agentcore-codeinterpreter>=0.0.1; extra == 'agentcore'
59
59
  Provides-Extra: all-providers
60
- Requires-Dist: langchain-anthropic<2.0.0,>=1.4.0; extra == 'all-providers'
60
+ Requires-Dist: langchain-anthropic<2.0.0,>=1.4.2; extra == 'all-providers'
61
61
  Requires-Dist: langchain-aws<2.0.0,>=1.0.0; extra == 'all-providers'
62
62
  Requires-Dist: langchain-baseten<1.0.0,>=0.1.9; extra == 'all-providers'
63
63
  Requires-Dist: langchain-cohere<1.0.0,>=0.5.0; extra == 'all-providers'
@@ -82,7 +82,7 @@ Requires-Dist: langchain-daytona>=0.0.4; extra == 'all-sandboxes'
82
82
  Requires-Dist: langchain-modal>=0.0.2; extra == 'all-sandboxes'
83
83
  Requires-Dist: langchain-runloop>=0.0.3; extra == 'all-sandboxes'
84
84
  Provides-Extra: anthropic
85
- Requires-Dist: langchain-anthropic<2.0.0,>=1.4.0; extra == 'anthropic'
85
+ Requires-Dist: langchain-anthropic<2.0.0,>=1.4.2; extra == 'anthropic'
86
86
  Provides-Extra: baseten
87
87
  Requires-Dist: langchain-baseten<1.0.0,>=0.1.9; extra == 'baseten'
88
88
  Provides-Extra: bedrock
@@ -52,6 +52,9 @@ real PyPI release. Any non-empty value enables the flag (including `"0"` or
52
52
  EXTRA_SKILLS_DIRS = "DEEPAGENTS_CLI_EXTRA_SKILLS_DIRS"
53
53
  """Colon-separated paths added to the skill containment allowlist."""
54
54
 
55
+ KITTY_KEYBOARD = "DEEPAGENTS_CLI_KITTY_KEYBOARD"
56
+ """Override kitty-keyboard detection (`1` forces on, `0` forces off)."""
57
+
55
58
  LANGSMITH_PROJECT = "DEEPAGENTS_CLI_LANGSMITH_PROJECT"
56
59
  """Override LangSmith project name for agent traces."""
57
60
 
@@ -0,0 +1,66 @@
1
+ r"""Preserve the `alt` modifier on legacy `ESC + <byte>` sequences.
2
+
3
+ Upstream `XTermParser._sequence_to_key_events` drops the `alt` flag on
4
+ the tuple-branch fast path, so VSCode's `sendSequence` shift+enter
5
+ binding (which writes `\x1b\r` to the PTY) arrives as bare `enter`
6
+ instead of `alt+enter`. Tracked in Textualize/textual#6378. Remove this
7
+ file and the Textual pin comment in `pyproject.toml` when that lands.
8
+
9
+ Imported for side effect from `app.py` before any `App()` is created.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ from typing import TYPE_CHECKING
16
+
17
+ if TYPE_CHECKING:
18
+ from collections.abc import Iterable
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ _ESC_PREFIX_LEN = 2
23
+
24
+ try:
25
+ from textual import events
26
+ from textual._ansi_sequences import (
27
+ ANSI_SEQUENCES_KEYS, # noqa: PLC2701
28
+ IGNORE_SEQUENCE, # noqa: PLC2701
29
+ )
30
+ from textual._xterm_parser import XTermParser # noqa: PLC2701
31
+
32
+ _original = XTermParser._sequence_to_key_events
33
+ except (ImportError, AttributeError) as exc: # pragma: no cover - defensive
34
+ logger.warning("Textual keyboard parser patch skipped: %s", exc)
35
+ else:
36
+
37
+ def _emit_alt(keys: tuple, character: str | None) -> Iterable[events.Key]:
38
+ for key in keys:
39
+ yield events.Key(f"alt+{key.value}", character)
40
+
41
+ def _sequence_to_key_events_with_alt(
42
+ self: XTermParser, sequence: str, alt: bool = False
43
+ ) -> Iterable[events.Key]:
44
+ # Fast path: \x1b<byte> on first pass. Short-circuits the ~100 ms
45
+ # escape-delay wait when both bytes arrive together. Semantic side
46
+ # effect: \x1b\x1b dispatches as `alt+escape` with no delay, matching
47
+ # crossterm and Node TTY.
48
+ if not alt and len(sequence) == _ESC_PREFIX_LEN and sequence[0] == "\x1b":
49
+ inner = ANSI_SEQUENCES_KEYS.get(sequence[1])
50
+ if inner is not IGNORE_SEQUENCE and isinstance(inner, tuple):
51
+ yield from _emit_alt(inner, None)
52
+ return
53
+ # Correctness fix (Textualize/textual#6378): preserve `alt` on the
54
+ # reissue path for single-byte tuple mappings.
55
+ if alt:
56
+ keys = ANSI_SEQUENCES_KEYS.get(sequence)
57
+ if keys is not IGNORE_SEQUENCE and isinstance(keys, tuple):
58
+ character = sequence if len(sequence) == 1 else None
59
+ yield from _emit_alt(keys, character)
60
+ return
61
+ yield from _original(self, sequence, alt=alt)
62
+
63
+ try:
64
+ XTermParser._sequence_to_key_events = _sequence_to_key_events_with_alt # ty: ignore[invalid-assignment]
65
+ except (AttributeError, TypeError) as exc: # pragma: no cover - defensive
66
+ logger.warning("Textual keyboard parser patch assignment rejected: %s", exc)
@@ -1,6 +1,6 @@
1
1
  """Version information and lightweight constants for `deepagents-cli`."""
2
2
 
3
- __version__ = "0.0.41" # x-release-please-version
3
+ __version__ = "0.0.42" # x-release-please-version
4
4
 
5
5
  DOCS_URL = "https://docs.langchain.com/oss/python/deepagents/cli"
6
6
  """URL for `deepagents-cli` documentation."""
@@ -16,6 +16,18 @@ from deepagents.backends import CompositeBackend, LocalShellBackend
16
16
  from deepagents.backends.filesystem import FilesystemBackend
17
17
  from deepagents.middleware import MemoryMiddleware, SkillsMiddleware
18
18
 
19
+ # Backwards-compat flag: SDKs before 0.5.4 accept only `list[str]` for
20
+ # `SkillsMiddleware.sources`; newer SDKs expose the `SkillSource` alias
21
+ # that permits `(path, label)` tuples. The `skills` module is already
22
+ # loaded by the `SkillsMiddleware` import above, so the extra lookup
23
+ # here adds no startup cost.
24
+ try:
25
+ from deepagents.middleware.skills import SkillSource as _SkillSource # noqa: F401
26
+ except ImportError:
27
+ _SUPPORTS_SKILL_SOURCE_TUPLES = False
28
+ else:
29
+ _SUPPORTS_SKILL_SOURCE_TUPLES = True
30
+
19
31
  if TYPE_CHECKING:
20
32
  from collections.abc import Awaitable, Callable, Sequence
21
33
 
@@ -1116,25 +1128,40 @@ def create_cli_agent(
1116
1128
  # built-in -> user .deepagents -> user .agents
1117
1129
  # -> project .deepagents -> project .agents
1118
1130
  # -> user .claude (experimental) -> project .claude (experimental)
1119
- sources = [str(settings.get_built_in_skills_dir())]
1120
- sources.extend([str(skills_dir), str(user_agent_skills_dir)])
1131
+ # Labels disambiguate user- vs project-scoped sources that share a
1132
+ # `.../skills` leaf; the middleware would otherwise derive identical
1133
+ # labels from the parent directory name.
1134
+ sources: list[tuple[str, str]] = [
1135
+ (str(settings.get_built_in_skills_dir()), "Built-in"),
1136
+ (str(skills_dir), "User Deepagents"),
1137
+ (str(user_agent_skills_dir), "User Agents"),
1138
+ ]
1121
1139
  if project_skills_dir:
1122
- sources.append(str(project_skills_dir))
1140
+ sources.append((str(project_skills_dir), "Project Deepagents"))
1123
1141
  if project_agent_skills_dir:
1124
- sources.append(str(project_agent_skills_dir))
1142
+ sources.append((str(project_agent_skills_dir), "Project Agents"))
1125
1143
 
1126
1144
  # Experimental: Claude Code skill directories
1127
1145
  user_claude_skills_dir = settings.get_user_claude_skills_dir()
1128
1146
  if user_claude_skills_dir.exists():
1129
- sources.append(str(user_claude_skills_dir))
1147
+ sources.append((str(user_claude_skills_dir), "User Claude"))
1130
1148
  project_claude_skills_dir = settings.get_project_claude_skills_dir()
1131
1149
  if project_claude_skills_dir:
1132
- sources.append(str(project_claude_skills_dir))
1150
+ sources.append((str(project_claude_skills_dir), "Project Claude"))
1151
+
1152
+ # Backwards-compat: strip labels when the installed SDK is too old
1153
+ # to accept `(path, label)` tuples. Label-based disambiguation
1154
+ # regresses to the pre-alias behavior (user- and project-scoped
1155
+ # `.claude/skills` collapse to the same label), but functionality
1156
+ # is preserved.
1157
+ middleware_sources: Sequence[str | tuple[str, str]] = (
1158
+ sources if _SUPPORTS_SKILL_SOURCE_TUPLES else [path for path, _ in sources]
1159
+ )
1133
1160
 
1134
1161
  agent_middleware.append(
1135
1162
  SkillsMiddleware(
1136
1163
  backend=FilesystemBackend(),
1137
- sources=sources,
1164
+ sources=middleware_sources,
1138
1165
  )
1139
1166
  )
1140
1167
 
@@ -33,7 +33,11 @@ from textual.widgets._toast import (
33
33
  Toast as _Toast, # noqa: PLC2701 # for Toast click routing
34
34
  )
35
35
 
36
- from deepagents_cli import theme
36
+ # Applied as an import-time side effect; must come before any App is created.
37
+ from deepagents_cli import (
38
+ _textual_patches, # noqa: F401
39
+ theme,
40
+ )
37
41
  from deepagents_cli._cli_context import CLIContext
38
42
  from deepagents_cli._git import (
39
43
  read_git_branch_from_filesystem,
@@ -6370,6 +6374,24 @@ class DeepAgentsApp(App):
6370
6374
  model_spec = model_spec.removeprefix(":")
6371
6375
 
6372
6376
  if not self._remote_agent():
6377
+ if self._connecting:
6378
+ from functools import partial
6379
+
6380
+ self._defer_action(
6381
+ DeferredAction(
6382
+ kind="model_switch",
6383
+ execute=partial(
6384
+ self._switch_model,
6385
+ model_spec,
6386
+ extra_kwargs=extra_kwargs,
6387
+ ),
6388
+ )
6389
+ )
6390
+ self.notify(
6391
+ "Model will switch once the session is ready.",
6392
+ timeout=3,
6393
+ )
6394
+ return
6373
6395
  await self._mount_message(
6374
6396
  ErrorMessage("Model switching requires a server-backed session.")
6375
6397
  )
@@ -496,14 +496,22 @@ def is_ascii_mode() -> bool:
496
496
 
497
497
 
498
498
  def newline_shortcut() -> str:
499
- """Return the platform-native label for the newline keyboard shortcut.
499
+ """Return the terminal-appropriate label for the newline keyboard shortcut.
500
500
 
501
- macOS labels the modifier "Option" while other platforms use Ctrl+J
502
- as the most reliable cross-terminal shortcut.
501
+ Prefers `Shift+Enter` when the terminal is known to support the kitty
502
+ keyboard protocol, either via conservative terminal-identity heuristics
503
+ or the `DEEPAGENTS_CLI_KITTY_KEYBOARD` override. Falls back to
504
+ `Option+Enter` on macOS and `Ctrl+J` elsewhere — both survive legacy
505
+ terminals that strip the shift modifier from `Enter`.
503
506
 
504
507
  Returns:
505
- A human-readable shortcut string, e.g. `'Option+Enter'` or `'Ctrl+J'`.
508
+ A human-readable shortcut string,
509
+ e.g. `'Shift+Enter'`, `'Option+Enter'`, or `'Ctrl+J'`.
506
510
  """
511
+ from deepagents_cli.terminal_capabilities import supports_kitty_keyboard_protocol
512
+
513
+ if supports_kitty_keyboard_protocol():
514
+ return "Shift+Enter"
507
515
  return "Option+Enter" if sys.platform == "darwin" else "Ctrl+J"
508
516
 
509
517
 
@@ -1854,36 +1862,52 @@ _OPENROUTER_APP_TITLE = "Deep Agents CLI"
1854
1862
  _OPENROUTER_APP_CATEGORIES: list[str] = ["cli-agent"]
1855
1863
  """Default `app_categories` (maps to `X-OpenRouter-Categories`) for OpenRouter."""
1856
1864
 
1865
+ _cli_openrouter_profile_registered = False
1866
+ """Process-wide guard so the CLI OpenRouter profile is registered exactly once."""
1857
1867
 
1858
- def _apply_openrouter_defaults(kwargs: dict[str, Any]) -> None:
1859
- """Inject default OpenRouter attribution kwargs.
1860
1868
 
1861
- Sets `app_url`, `app_title`, and `app_categories` via `setdefault` so
1862
- that user-supplied values in config take precedence. These map to the
1863
- `HTTP-Referer`, `X-Title`, and `X-OpenRouter-Categories` headers that
1864
- `ChatOpenRouter` sends for app attribution
1865
- (see https://openrouter.ai/docs/app-attribution).
1869
+ def _cli_openrouter_attribution_kwargs() -> dict[str, Any]:
1870
+ """CLI-specific OpenRouter attribution kwargs.
1866
1871
 
1867
- Users can override either value provider-wide or per-model in
1868
- `~/.deepagents/config.toml`:
1872
+ Layered on top of the SDK's built-in factory via profile stacking; these
1873
+ values override the SDK defaults but still sit beneath any caller-supplied
1874
+ `kwargs` (i.e. `config.toml`-resolved values), preserving the precedence
1875
+ documented on `apply_provider_profile`.
1869
1876
 
1870
- ```toml
1871
- # Provider-wide
1872
- [models.providers.openrouter.params]
1873
- app_url = "https://myapp.com"
1874
- app_title = "My App"
1877
+ Returns:
1878
+ Mapping of `app_url` and `app_title` to spread into `init_chat_model`.
1879
+ """
1880
+ return {
1881
+ "app_url": _OPENROUTER_APP_URL,
1882
+ "app_title": _OPENROUTER_APP_TITLE,
1883
+ }
1875
1884
 
1876
- # Per-model (shallow-merges on top of provider-wide)
1877
- [models.providers.openrouter.params."openai/gpt-oss-120b"]
1878
- app_title = "My App (GPT)"
1879
- ```
1880
1885
 
1881
- Args:
1882
- kwargs: Mutable kwargs dict to update in place.
1886
+ def _ensure_cli_openrouter_profile_registered() -> None:
1887
+ """Stack the CLI OpenRouter attribution onto the SDK's built-in profile.
1888
+
1889
+ Stacking (vs. duplicating the inline `_get_provider_kwargs` path) means the
1890
+ SDK's `pre_init` version check fires exactly once and the CLI's app-
1891
+ attribution defaults are composed via the same `apply_provider_profile`
1892
+ path used for every other provider. `register_provider_profile` merges on
1893
+ top of the existing built-in registration: the CLI's `init_kwargs` and
1894
+ factory output win on shared keys, while the built-in's `pre_init` and
1895
+ factory still chain.
1883
1896
  """
1884
- kwargs.setdefault("app_url", _OPENROUTER_APP_URL)
1885
- kwargs.setdefault("app_title", _OPENROUTER_APP_TITLE)
1886
- kwargs.setdefault("app_categories", _OPENROUTER_APP_CATEGORIES)
1897
+ global _cli_openrouter_profile_registered # noqa: PLW0603
1898
+ if _cli_openrouter_profile_registered:
1899
+ return
1900
+
1901
+ from deepagents.profiles.provider import ProviderProfile, register_provider_profile
1902
+
1903
+ register_provider_profile(
1904
+ "openrouter",
1905
+ ProviderProfile(
1906
+ init_kwargs={"app_categories": _OPENROUTER_APP_CATEGORIES},
1907
+ init_kwargs_factory=_cli_openrouter_attribution_kwargs,
1908
+ ),
1909
+ )
1910
+ _cli_openrouter_profile_registered = True
1887
1911
 
1888
1912
 
1889
1913
  def _get_provider_kwargs(
@@ -1927,14 +1951,6 @@ def _get_provider_kwargs(
1927
1951
  if api_key:
1928
1952
  result["api_key"] = api_key
1929
1953
 
1930
- if provider == "openrouter":
1931
- from deepagents.profiles._openrouter import (
1932
- check_openrouter_version, # noqa: PLC2701
1933
- )
1934
-
1935
- check_openrouter_version()
1936
- _apply_openrouter_defaults(result)
1937
-
1938
1954
  return result
1939
1955
 
1940
1956
 
@@ -2242,6 +2258,37 @@ def create_model(
2242
2258
  # Provider-specific kwargs (with per-model overrides)
2243
2259
  kwargs = _get_provider_kwargs(provider, model_name=model_name)
2244
2260
 
2261
+ # Compose under existing kwargs: profile < config.toml < --model-params
2262
+ # (applied below). The CLI's OpenRouter profile is stacked on top of the
2263
+ # built-in SDK profile so its `pre_init` (version check) and factory
2264
+ # (app attribution) compose into a single `apply_provider_profile` call.
2265
+ if provider:
2266
+ from deepagents.profiles.provider import apply_provider_profile
2267
+
2268
+ if provider == "openrouter":
2269
+ _ensure_cli_openrouter_profile_registered()
2270
+
2271
+ spec = f"{provider}:{model_name}" if model_name else provider
2272
+ try:
2273
+ kwargs = apply_provider_profile(spec, kwargs)
2274
+ except ModelConfigError:
2275
+ raise
2276
+ except Exception as exc:
2277
+ # `pre_init` and `init_kwargs_factory` callables registered on a
2278
+ # `ProviderProfile` may raise arbitrary exceptions (e.g. an
2279
+ # `ImportError` from the OpenRouter min-version check). Surface
2280
+ # them as `ModelConfigError` so the CLI's error path renders an
2281
+ # actionable message instead of a raw stack trace.
2282
+ logger.debug(
2283
+ "ProviderProfile resolution for %r failed.", spec, exc_info=True
2284
+ )
2285
+ msg = (
2286
+ f"Failed to apply provider profile for '{spec}': {exc}. "
2287
+ f"Check that the provider package is installed and up to date, "
2288
+ f"or set explicit kwargs via `--model-params`."
2289
+ )
2290
+ raise ModelConfigError(msg) from exc
2291
+
2245
2292
  # CLI --model-params take highest priority
2246
2293
  if extra_kwargs:
2247
2294
  kwargs.update(extra_kwargs)
@@ -224,7 +224,7 @@ if command -v node >/dev/null 2>&1; then
224
224
  NV="$(node --version 2>/dev/null | sed 's/^v//')"
225
225
  [ -n "$NV" ] && RT="${RT:+${RT}, }Node ${NV}"
226
226
  fi
227
- [ -n "$RT" ] && echo "**Runtimes**: ${RT}" && echo ""
227
+ [ -n "$RT" ] && echo "**Detected Runtimes**: ${RT}" && echo ""
228
228
  """
229
229
 
230
230
 
@@ -246,7 +246,7 @@ if $IN_GIT; then
246
246
  master) MAINS="${MAINS:+${MAINS}, }\`master\`" ;;
247
247
  esac
248
248
  done
249
- [ -n "$MAINS" ] && GT="${GT}, main branch available: ${MAINS}"
249
+ [ -n "$MAINS" ] && GT="${GT}, ${MAINS} available"
250
250
 
251
251
  DC=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
252
252
  if [ "$DC" -gt 0 ]; then
@@ -301,13 +301,18 @@ if [ -n "$FILES" ]; then
301
301
  TOTAL=$(echo "$FILES" | wc -l | tr -d ' ')
302
302
  SHOWN_FILES=$(echo "$FILES" | head -20)
303
303
  SHOWN=$(echo "$SHOWN_FILES" | wc -l | tr -d ' ')
304
- echo "**Files** (${SHOWN} shown):"
304
+ TOTAL=${TOTAL:-0}
305
+ SHOWN=${SHOWN:-0}
306
+ if [ "$SHOWN" -lt "$TOTAL" ]; then
307
+ echo "**Files** (showing ${SHOWN} of ${TOTAL}):"
308
+ else
309
+ echo "**Files** (${TOTAL}):"
310
+ fi
305
311
  echo "$SHOWN_FILES" | while IFS= read -r f; do
306
312
  if [ -d "$f" ]; then echo "- ${f}/"
307
313
  else echo "- ${f}"
308
314
  fi
309
315
  done
310
- [ "$SHOWN" -lt "$TOTAL" ] && echo "... ($((TOTAL - SHOWN)) more files)"
311
316
  echo ""
312
317
  fi"""
313
318
 
@@ -323,12 +328,21 @@ if command -v tree >/dev/null 2>&1; then
323
328
  TREE_EXCL='node_modules|.venv|__pycache__|.pytest_cache'
324
329
  TREE_EXCL="${TREE_EXCL}|.git|.mypy_cache|.ruff_cache"
325
330
  TREE_EXCL="${TREE_EXCL}|.tox|.coverage|.eggs|dist|build"
326
- T=$(tree -L 3 --noreport --dirsfirst \
327
- -I "$TREE_EXCL" 2>/dev/null | head -22)
328
- if [ -n "$T" ]; then
331
+ T_PREVIEW=$(tree -L 3 --noreport --dirsfirst \
332
+ -I "$TREE_EXCL" 2>/dev/null | sed -n '1,22p;23{p;q;}')
333
+ if [ -n "$T_PREVIEW" ]; then
334
+ PREVIEW_LINES=$(echo "$T_PREVIEW" | wc -l | tr -d ' ')
335
+ PREVIEW_LINES=${PREVIEW_LINES:-0}
336
+ T="$T_PREVIEW"
337
+ TREE_TRUNCATED=false
338
+ if [ "$PREVIEW_LINES" -gt 22 ]; then
339
+ T=$(echo "$T_PREVIEW" | head -22)
340
+ TREE_TRUNCATED=true
341
+ fi
329
342
  echo "**Tree** (3 levels):"
330
343
  echo '```text'
331
344
  echo "$T"
345
+ $TREE_TRUNCATED && echo "... (more lines truncated)"
332
346
  echo '```'
333
347
  echo ""
334
348
  fi
@@ -1,6 +1,6 @@
1
1
  # Deep Agents CLI
2
2
 
3
- You are a Deep Agent, an AI assistant running in {mode_description}. You help with tasks like coding, debugging, research, analysis, and more.
3
+ You are a deep agent, an AI assistant running in {mode_description}. You help with tasks like coding, debugging, research, analysis, and more.
4
4
 
5
5
  {interactive_preamble}
6
6
 
@@ -0,0 +1,115 @@
1
+ """Terminal capability detection.
2
+
3
+ Detect optional terminal features without reading from `stdin`.
4
+
5
+ The CLI only uses kitty-keyboard-protocol support to choose a user-facing
6
+ newline shortcut label. To keep startup safe on remote or high-latency PTYs,
7
+ detection is conservative and relies on side-effect-free terminal identity
8
+ signals plus an explicit environment-variable override.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import os
15
+ import sys
16
+ from functools import cache
17
+ from typing import TYPE_CHECKING
18
+
19
+ from deepagents_cli._env_vars import KITTY_KEYBOARD
20
+
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Mapping
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ _TRUE_VALUES = frozenset({"1", "true", "yes", "on"})
27
+ _FALSE_VALUES = frozenset({"0", "false", "no", "off"})
28
+ _KNOWN_KITTY_KEYBOARD_TERMS = frozenset({"xterm-ghostty", "xterm-kitty"})
29
+
30
+
31
+ def _override_supports_kitty_keyboard_protocol(
32
+ env: Mapping[str, str],
33
+ ) -> bool | None:
34
+ """Return an explicit kitty-keyboard override from `env`, if present.
35
+
36
+ Accepted truthy values are `'1'`, `'true'`, `'yes'`, and `'on'`.
37
+ Accepted falsy values are `'0'`, `'false'`, `'no'`, and `'off'`.
38
+ `'auto'`, the empty string, and invalid values fall back to heuristic
39
+ detection.
40
+ """
41
+ raw = env.get(KITTY_KEYBOARD)
42
+ if raw is None:
43
+ return None
44
+
45
+ normalized = raw.strip().lower()
46
+ if normalized in {"", "auto"}:
47
+ return None
48
+ if normalized in _TRUE_VALUES:
49
+ return True
50
+ if normalized in _FALSE_VALUES:
51
+ return False
52
+
53
+ logger.warning(
54
+ "%s=%r ignored; expected one of: %s, or 'auto' to defer to detection.",
55
+ KITTY_KEYBOARD,
56
+ raw,
57
+ ", ".join(sorted(_TRUE_VALUES | _FALSE_VALUES)),
58
+ )
59
+ return None
60
+
61
+
62
+ def _terminal_identity_supports_kitty_keyboard_protocol(
63
+ env: Mapping[str, str],
64
+ ) -> bool:
65
+ """Return whether `env` identifies a terminal with built-in kitty support.
66
+
67
+ This intentionally only recognizes terminals whose environment markers
68
+ imply kitty-keyboard support is part of the terminal's default identity.
69
+ Configurable terminals such as iTerm2 and WezTerm are intentionally not
70
+ auto-detected because protocol support can be disabled in user settings.
71
+ """
72
+ if env.get("KITTY_WINDOW_ID"):
73
+ return True
74
+
75
+ term = env.get("TERM", "")
76
+ return term in _KNOWN_KITTY_KEYBOARD_TERMS
77
+
78
+
79
+ @cache
80
+ def supports_kitty_keyboard_protocol() -> bool:
81
+ """Return whether the attached terminal should be treated as kitty-aware.
82
+
83
+ Detection is side-effect free: it never writes escape sequences or reads
84
+ queued input bytes. That means it may under-detect some configurable
85
+ terminals, but it will not interfere with Textual's input stream.
86
+
87
+ Set `DEEPAGENTS_CLI_KITTY_KEYBOARD` to an accepted truthy value (`1`,
88
+ `true`, `yes`, `on`) to force-enable the label, a falsy value (`0`,
89
+ `false`, `no`, `off`) to force-disable it, or `auto`/unset to use
90
+ heuristic detection.
91
+
92
+ Returns:
93
+ `True` when the terminal is known to support the kitty keyboard
94
+ protocol, `False` otherwise.
95
+ """
96
+ if sys.platform == "win32":
97
+ logger.debug("kitty kbd detection: False (win32 unsupported)")
98
+ return False
99
+ if not (sys.stdin.isatty() and sys.stdout.isatty()):
100
+ logger.debug("kitty kbd detection: False (stdin/stdout not a tty)")
101
+ return False
102
+
103
+ override = _override_supports_kitty_keyboard_protocol(os.environ)
104
+ if override is not None:
105
+ logger.debug("kitty kbd detection: %s (explicit override)", override)
106
+ return override
107
+
108
+ detected = _terminal_identity_supports_kitty_keyboard_protocol(os.environ)
109
+ logger.debug(
110
+ "kitty kbd detection: %s (terminal identity TERM=%r KITTY_WINDOW_ID=%r)",
111
+ detected,
112
+ os.environ.get("TERM", ""),
113
+ os.environ.get("KITTY_WINDOW_ID"),
114
+ )
115
+ return detected
@@ -471,8 +471,29 @@ class ModelSelectorScreen(ModalScreen[tuple[str, str] | None]):
471
471
  self._option_widgets = []
472
472
 
473
473
  if not self._filtered_models:
474
- msg = "Loading models…" if not self._loaded else "No matching models"
475
- await self._options_container.mount(Static(Content.styled(msg, "dim")))
474
+ if not self._loaded:
475
+ empty_content: Content = Content.styled("Loading models…", "dim")
476
+ else:
477
+ typed = self._filter_text.strip()
478
+ if typed and ":" in typed:
479
+ empty_content = Content.assemble(
480
+ ("No matching models — press ", "dim"),
481
+ ("Enter", "bold"),
482
+ (" to use ", "dim"),
483
+ (typed, "bold"),
484
+ (" as a custom provider:model spec", "dim"),
485
+ )
486
+ elif typed:
487
+ empty_content = Content.assemble(
488
+ ("No matching models — press ", "dim"),
489
+ ("Enter", "bold"),
490
+ (" to use ", "dim"),
491
+ (typed, "bold"),
492
+ (" as a custom model spec (no provider prefix)", "dim"),
493
+ )
494
+ else:
495
+ empty_content = Content.styled("No matching models", "dim")
496
+ await self._options_container.mount(Static(empty_content))
476
497
  self._update_footer()
477
498
  return
478
499