deepagents-code 0.1.28__tar.gz → 0.1.29__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 (313) hide show
  1. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/CHANGELOG.md +13 -0
  2. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/COMMANDS.md +2 -1
  3. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/PKG-INFO +2 -2
  4. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_build_info.py +1 -1
  5. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_env_vars.py +12 -0
  6. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_server_config.py +22 -10
  7. deepagents_code-0.1.29/deepagents_code/_testing_models.py +317 -0
  8. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_version.py +1 -1
  9. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/agent.py +90 -17
  10. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/app.py +517 -38
  11. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/app.tcss +5 -2
  12. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/command_registry.py +12 -4
  13. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/config.py +37 -10
  14. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/config_manifest.py +9 -0
  15. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/main.py +2 -2
  16. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/non_interactive.py +27 -12
  17. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/server.py +28 -0
  18. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/server_manager.py +6 -4
  19. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/textual_adapter.py +15 -9
  20. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/ui.py +1 -1
  21. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/chat_input.py +1 -1
  22. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/model_selector.py +2 -2
  23. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/status.py +1 -1
  24. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/pyproject.toml +2 -2
  25. deepagents_code-0.1.29/tests/integration_tests/test_auto_approve_remote.py +198 -0
  26. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_agent.py +71 -0
  27. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_app.py +926 -27
  28. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_command_registry.py +24 -0
  29. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_model_selector.py +24 -1
  30. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_rubric_cli.py +15 -7
  31. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server.py +103 -0
  32. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server_config.py +3 -3
  33. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server_graph.py +1 -1
  34. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_textual_adapter.py +97 -22
  35. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/uv.lock +1 -1
  36. deepagents_code-0.1.28/deepagents_code/_testing_models.py +0 -144
  37. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/.gitignore +0 -0
  38. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/AGENTS.md +0 -0
  39. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/ARCHITECTURE.md +0 -0
  40. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/DEVELOPMENT.md +0 -0
  41. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/Makefile +0 -0
  42. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/README.md +0 -0
  43. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/THREAT_MODEL.md +0 -0
  44. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/__init__.py +0 -0
  45. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/__main__.py +0 -0
  46. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_ask_user_types.py +0 -0
  47. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_cli_context.py +0 -0
  48. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_constants.py +0 -0
  49. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_debug.py +0 -0
  50. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_git.py +0 -0
  51. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_paths.py +0 -0
  52. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_session_stats.py +0 -0
  53. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_startup_error.py +0 -0
  54. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_textual_patches.py +0 -0
  55. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/approval_mode.py +0 -0
  56. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/ask_user.py +0 -0
  57. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/auth_commands.py +0 -0
  58. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/auth_display.py +0 -0
  59. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/auth_store.py +0 -0
  60. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/__init__.py +0 -0
  61. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
  62. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
  63. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  64. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  65. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/clipboard.py +0 -0
  66. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/config_commands.py +0 -0
  67. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/configurable_model.py +0 -0
  68. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/default_agent_prompt.md +0 -0
  69. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/doctor.py +0 -0
  70. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/editor.py +0 -0
  71. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/event_bus.py +0 -0
  72. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/extras_info.py +0 -0
  73. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/file_ops.py +0 -0
  74. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/formatting.py +0 -0
  75. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/goal_rubric.py +0 -0
  76. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/goal_tools.py +0 -0
  77. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/hooks.py +0 -0
  78. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/input.py +0 -0
  79. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/__init__.py +0 -0
  80. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/openai_codex.py +0 -0
  81. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/sandbox_config.py +0 -0
  82. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/sandbox_factory.py +0 -0
  83. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/sandbox_provider.py +0 -0
  84. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/sandbox_registry.py +0 -0
  85. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/iterm_cursor_guide.py +0 -0
  86. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/local_context.py +0 -0
  87. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/managed_tools.py +0 -0
  88. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_auth.py +0 -0
  89. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_commands.py +0 -0
  90. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_disabled.py +0 -0
  91. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_login_service.py +0 -0
  92. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_oauth_ui.py +0 -0
  93. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/__init__.py +0 -0
  94. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/_registry.py +0 -0
  95. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/base.py +0 -0
  96. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/github.py +0 -0
  97. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/slack.py +0 -0
  98. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_tools.py +0 -0
  99. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_trust.py +0 -0
  100. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/media_utils.py +0 -0
  101. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/memory_guard.py +0 -0
  102. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/model_config.py +0 -0
  103. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/notifications.py +0 -0
  104. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/offload.py +0 -0
  105. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/onboarding.py +0 -0
  106. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/output.py +0 -0
  107. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/project_utils.py +0 -0
  108. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/py.typed +0 -0
  109. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/remote_client.py +0 -0
  110. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/resume_state.py +0 -0
  111. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/server_graph.py +0 -0
  112. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/sessions.py +0 -0
  113. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/skills/__init__.py +0 -0
  114. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/skills/commands.py +0 -0
  115. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/skills/invocation.py +0 -0
  116. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/skills/load.py +0 -0
  117. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/state_migration.py +0 -0
  118. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/subagents.py +0 -0
  119. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/system_prompt.md +0 -0
  120. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/terminal_capabilities.py +0 -0
  121. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/terminal_escape.py +0 -0
  122. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/theme.py +0 -0
  123. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/tool_display.py +0 -0
  124. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/tools.py +0 -0
  125. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/tools_commands.py +0 -0
  126. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/unicode_security.py +0 -0
  127. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/update_check.py +0 -0
  128. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/__init__.py +0 -0
  129. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/_js_eval_display.py +0 -0
  130. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/_links.py +0 -0
  131. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/agent_selector.py +0 -0
  132. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/approval.py +0 -0
  133. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/ask_user.py +0 -0
  134. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/auth.py +0 -0
  135. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/autocomplete.py +0 -0
  136. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/codex_auth.py +0 -0
  137. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/cwd_switch.py +0 -0
  138. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/diff.py +0 -0
  139. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/goal_review.py +0 -0
  140. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/history.py +0 -0
  141. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/install_confirm.py +0 -0
  142. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/launch_init.py +0 -0
  143. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/loading.py +0 -0
  144. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/mcp_login.py +0 -0
  145. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/mcp_reconnect.py +0 -0
  146. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/mcp_viewer.py +0 -0
  147. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/message_store.py +0 -0
  148. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/messages.py +0 -0
  149. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/notification_center.py +0 -0
  150. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/notification_detail.py +0 -0
  151. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/notification_settings.py +0 -0
  152. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/restart_prompt.py +0 -0
  153. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/subagent_panel.py +0 -0
  154. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/theme_selector.py +0 -0
  155. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/thread_selector.py +0 -0
  156. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/tool_renderers.py +0 -0
  157. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/tool_widgets.py +0 -0
  158. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/update_available.py +0 -0
  159. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/update_confirm.py +0 -0
  160. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/update_progress.py +0 -0
  161. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/welcome.py +0 -0
  162. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/arxiv-search/SKILL.md +0 -0
  163. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  164. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/langgraph-docs/SKILL.md +0 -0
  165. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/skill-creator/SKILL.md +0 -0
  166. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  167. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  168. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/web-research/SKILL.md +0 -0
  169. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/hatch_build.py +0 -0
  170. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/images/tui.png +0 -0
  171. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/scripts/check_imports.py +0 -0
  172. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/scripts/debug_server.sh +0 -0
  173. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/scripts/generate_commands_catalog.py +0 -0
  174. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/scripts/install.sh +0 -0
  175. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/README.md +0 -0
  176. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/__init__.py +0 -0
  177. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/benchmarks/__init__.py +0 -0
  178. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  179. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  180. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/conftest.py +0 -0
  181. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/test_acp_mode.py +0 -0
  182. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/test_compact_resume.py +0 -0
  183. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/test_sandbox_factory.py +0 -0
  184. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/test_sandbox_operations.py +0 -0
  185. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/__init__.py +0 -0
  186. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/conftest.py +0 -0
  187. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/data/coding_agent_v1_validator.json +0 -0
  188. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/skills/__init__.py +0 -0
  189. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/skills/test_commands.py +0 -0
  190. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/skills/test_load.py +0 -0
  191. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/skills/test_skills_json.py +0 -0
  192. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_agent_friendly.py +0 -0
  193. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_agent_selector.py +0 -0
  194. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_approval.py +0 -0
  195. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_approval_mode.py +0 -0
  196. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_args.py +0 -0
  197. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_ask_user.py +0 -0
  198. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  199. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_auth_commands.py +0 -0
  200. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_auth_display.py +0 -0
  201. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_auth_store.py +0 -0
  202. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_auth_widgets.py +0 -0
  203. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_autocomplete.py +0 -0
  204. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_charset.py +0 -0
  205. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_chat_input.py +0 -0
  206. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_clipboard.py +0 -0
  207. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_coding_agent_metadata.py +0 -0
  208. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_compact_tool.py +0 -0
  209. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_config.py +0 -0
  210. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_config_manifest.py +0 -0
  211. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_configurable_model.py +0 -0
  212. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_cursor_blink.py +0 -0
  213. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_cwd_switch.py +0 -0
  214. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_debug.py +0 -0
  215. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_doctor.py +0 -0
  216. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_editor.py +0 -0
  217. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_end_to_end.py +0 -0
  218. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_env_vars.py +0 -0
  219. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_event_bus.py +0 -0
  220. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_exception_handling.py +0 -0
  221. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_extras_info.py +0 -0
  222. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_file_ops.py +0 -0
  223. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_formatting.py +0 -0
  224. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_git.py +0 -0
  225. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_goal_review.py +0 -0
  226. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_goal_rubric.py +0 -0
  227. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_goal_tools.py +0 -0
  228. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_hatch_build.py +0 -0
  229. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_history.py +0 -0
  230. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_hooks.py +0 -0
  231. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_imports.py +0 -0
  232. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_input_parsing.py +0 -0
  233. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_install_command.py +0 -0
  234. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_install_confirm.py +0 -0
  235. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_install_script.py +0 -0
  236. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
  237. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_launch_init.py +0 -0
  238. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_links.py +0 -0
  239. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_loading.py +0 -0
  240. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_local_context.py +0 -0
  241. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_main.py +0 -0
  242. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_main_acp_mode.py +0 -0
  243. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_main_args.py +0 -0
  244. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_managed_tools.py +0 -0
  245. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_auth.py +0 -0
  246. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_commands.py +0 -0
  247. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_disabled.py +0 -0
  248. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_login_modal.py +0 -0
  249. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_login_service.py +0 -0
  250. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
  251. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_reconnect.py +0 -0
  252. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_tools.py +0 -0
  253. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_trust.py +0 -0
  254. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_viewer.py +0 -0
  255. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_media_utils.py +0 -0
  256. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_memory_guard.py +0 -0
  257. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_message_store.py +0 -0
  258. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_messages.py +0 -0
  259. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_model_config.py +0 -0
  260. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_model_switch.py +0 -0
  261. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_non_interactive.py +0 -0
  262. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_notification_center.py +0 -0
  263. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_notification_detail.py +0 -0
  264. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_notifications.py +0 -0
  265. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_offload.py +0 -0
  266. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  267. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_onboarding.py +0 -0
  268. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_openai_codex_integration.py +0 -0
  269. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_output.py +0 -0
  270. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_paths.py +0 -0
  271. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_reload.py +0 -0
  272. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_remote_client.py +0 -0
  273. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_restart_prompt.py +0 -0
  274. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_resume_state.py +0 -0
  275. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sandbox_config.py +0 -0
  276. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sandbox_factory.py +0 -0
  277. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sandbox_provider.py +0 -0
  278. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sandbox_registry.py +0 -0
  279. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server_helpers.py +0 -0
  280. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server_manager.py +0 -0
  281. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_session_stats.py +0 -0
  282. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sessions.py +0 -0
  283. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_shell_allow_list.py +0 -0
  284. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_skill_invocation.py +0 -0
  285. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_startup_fast_paths.py +0 -0
  286. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_state_migration.py +0 -0
  287. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_status.py +0 -0
  288. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_subagent_panel.py +0 -0
  289. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_subagent_stream.py +0 -0
  290. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_subagents.py +0 -0
  291. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  292. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_terminal_escape.py +0 -0
  293. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_terminal_progress_preference.py +0 -0
  294. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_textual_patches.py +0 -0
  295. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_theme.py +0 -0
  296. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_thread_selector.py +0 -0
  297. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_tool_display.py +0 -0
  298. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_tool_renderers.py +0 -0
  299. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_tools_commands.py +0 -0
  300. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_tracing_replicas.py +0 -0
  301. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_ui.py +0 -0
  302. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_unicode_security.py +0 -0
  303. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_update_available.py +0 -0
  304. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_update_check.py +0 -0
  305. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_update_confirm.py +0 -0
  306. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_update_progress.py +0 -0
  307. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_version.py +0 -0
  308. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_welcome.py +0 -0
  309. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/tools/__init__.py +0 -0
  310. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/tools/test_current_thread.py +0 -0
  311. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/tools/test_fetch_url.py +0 -0
  312. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/widgets/__init__.py +0 -0
  313. {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/widgets/test_diff.py +0 -0
@@ -2,6 +2,19 @@
2
2
 
3
3
  # Deep Agents Code Changelog
4
4
 
5
+ ## [0.1.29](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.28...deepagents-code==0.1.29) (2026-07-01)
6
+
7
+ ### Features
8
+
9
+ * Add rubric iteration controls ([#4405](https://github.com/langchain-ai/deepagents/issues/4405)) ([d6692a7](https://github.com/langchain-ai/deepagents/commit/d6692a7c713490f170b17510d613e02ee37574ab))
10
+ * Add Claude Sonnet 5 to model picker ([#4386](https://github.com/langchain-ai/deepagents/issues/4386)) ([64758dd](https://github.com/langchain-ai/deepagents/commit/64758dddf8b46c5b68c345fb1eac1fb7bcbf7f7e))
11
+
12
+ ### Bug Fixes
13
+
14
+ * Full-width chat messages, hide scrollbar, flush input bg ([#4374](https://github.com/langchain-ai/deepagents/issues/4374)) ([1f8e8dc](https://github.com/langchain-ai/deepagents/commit/1f8e8dc942ac54c4014ac48f9345ee0201be0b1b))
15
+ * Resolve editable SDK version metadata ([#4394](https://github.com/langchain-ai/deepagents/issues/4394)) ([3239bf4](https://github.com/langchain-ai/deepagents/commit/3239bf4edf2be3037bb18315463ac6a0c3537e5c))
16
+ * Unblock MCP force reconnect modal ([#4396](https://github.com/langchain-ai/deepagents/issues/4396)) ([8b7eab0](https://github.com/langchain-ai/deepagents/commit/8b7eab023db686f577d16f4f9ac872205baf136f))
17
+
5
18
  ## [0.1.28](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.27...deepagents-code==0.1.28) (2026-06-29)
6
19
 
7
20
  ### Features
@@ -8,7 +8,7 @@ Regenerate this file with `make commands-catalog` after changing command names,
8
8
  aliases, descriptions, visibility, or hidden-command metadata.
9
9
 
10
10
 
11
- ## Public (30)
11
+ ## Public (31)
12
12
 
13
13
  | Command | Aliases | Description |
14
14
  | --- | --- | --- |
@@ -34,6 +34,7 @@ aliases, descriptions, visibility, or hidden-command metadata.
34
34
  | `/remember` | | Save useful context to memory or skills |
35
35
  | `/restart` | | Restart the agent server |
36
36
  | `/rubric` | `/criteria` | Set explicit acceptance criteria for rubric grading |
37
+ | `/scrollbar` | | Show or hide the chat scrollbar |
37
38
  | `/skill-creator` | | Create or refine agent skills |
38
39
  | `/theme` | | Change color theme |
39
40
  | `/threads` | | Browse and resume past threads |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents-code
3
- Version: 0.1.28
3
+ Version: 0.1.29
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,7 +27,7 @@ Classifier: Topic :: Terminals
27
27
  Requires-Python: <4.0,>=3.11
28
28
  Requires-Dist: aiosqlite<1.0.0,>=0.22.1
29
29
  Requires-Dist: deepagents-acp<1.0.0,>=0.0.8
30
- Requires-Dist: deepagents==0.7.0a2
30
+ Requires-Dist: deepagents==0.7.0a3
31
31
  Requires-Dist: httpx<1.0.0,>=0.28.1
32
32
  Requires-Dist: langchain-anthropic<2.0.0,>=1.4.7
33
33
  Requires-Dist: langchain-google-genai<5.0.0,>=4.2.6
@@ -1,3 +1,3 @@
1
1
  """Generated at build time. Do not edit or commit."""
2
2
 
3
- BUILD_COMMIT = "14a0fb1"
3
+ BUILD_COMMIT = "c540098"
@@ -206,6 +206,18 @@ Defaults to enabled; set to a falsy value (`0`, `false`, `no`, `off`, or empty)
206
206
  to hide replica tracing details from the splash while leaving tracing active.
207
207
  """
208
208
 
209
+ SHOW_SCROLLBAR = "DEEPAGENTS_CODE_SHOW_SCROLLBAR"
210
+ """Show the vertical scrollbar in the chat area when enabled.
211
+
212
+ Off by default; use the `/scrollbar` slash command or `[ui].show_scrollbar` in
213
+ config.toml to toggle. Parsed by `classify_env_bool` (an unrecognized or empty
214
+ value falls through to the config value rather than forcing the default).
215
+
216
+ When set, this env var takes precedence over the persisted `[ui].show_scrollbar`
217
+ config value on launch, so a `/scrollbar` toggle will not appear to "stick"
218
+ across restarts while the env var remains set.
219
+ """
220
+
209
221
  THEME = "DEEPAGENTS_CODE_THEME"
210
222
  """Force the CLI to launch with this theme name when set."""
211
223
 
@@ -80,7 +80,7 @@ def _read_env_str(suffix: str) -> str | None:
80
80
  return os.environ.get(f"{SERVER_ENV_PREFIX}{suffix}")
81
81
 
82
82
 
83
- def _read_env_int(suffix: str, *, default: int) -> int:
83
+ def _read_env_int(suffix: str, *, default: int | None) -> int | None:
84
84
  """Read a `DEEPAGENTS_CODE_SERVER_*` integer from the environment.
85
85
 
86
86
  Args:
@@ -88,7 +88,7 @@ def _read_env_int(suffix: str, *, default: int) -> int:
88
88
  default: Value when the variable is absent or malformed.
89
89
 
90
90
  Returns:
91
- Parsed integer, or the default when parsing fails.
91
+ Parsed integer, or the default when absent or parsing fails.
92
92
  """
93
93
  raw = os.environ.get(f"{SERVER_ENV_PREFIX}{suffix}")
94
94
  if raw is None:
@@ -264,9 +264,8 @@ class ServerConfig:
264
264
  `None` reuses the main agent model.
265
265
  """
266
266
 
267
- rubric_max_iterations: int = 3
268
- """Grader iterations per rubric attempt before the agent stops with
269
- `'max_iterations_reached'`."""
267
+ rubric_max_iterations: int | None = None
268
+ """Explicit grader iterations per rubric attempt; `None` uses the SDK default."""
270
269
 
271
270
  sandbox_type: str | None = None
272
271
  """Sandbox backend identifier (e.g. `'daytona'`); `None` runs tools on the
@@ -304,13 +303,21 @@ class ServerConfig:
304
303
  """Normalize fields and validate invariants.
305
304
 
306
305
  Raises:
307
- ValueError: If `shell_allow_list` is an empty list.
306
+ TypeError: If `rubric_max_iterations` is a boolean.
307
+ ValueError: If `shell_allow_list` is an empty list or
308
+ `rubric_max_iterations` is non-positive.
308
309
  """
309
310
  if self.sandbox_type == "none":
310
311
  object.__setattr__(self, "sandbox_type", None)
311
312
  if self.shell_allow_list is not None and len(self.shell_allow_list) == 0:
312
313
  msg = "shell_allow_list must be None or non-empty"
313
314
  raise ValueError(msg)
315
+ if isinstance(self.rubric_max_iterations, bool):
316
+ msg = "rubric_max_iterations must be None or a positive integer"
317
+ raise TypeError(msg)
318
+ if self.rubric_max_iterations is not None and self.rubric_max_iterations <= 0:
319
+ msg = "rubric_max_iterations must be None or a positive integer"
320
+ raise ValueError(msg)
314
321
 
315
322
  # ------------------------------------------------------------------
316
323
  # Serialization
@@ -356,7 +363,11 @@ class ServerConfig:
356
363
  self.interpreter_ptc_acknowledge_unsafe
357
364
  ).lower(),
358
365
  "RUBRIC_MODEL": self.rubric_model,
359
- "RUBRIC_MAX_ITERATIONS": str(self.rubric_max_iterations),
366
+ "RUBRIC_MAX_ITERATIONS": (
367
+ str(self.rubric_max_iterations)
368
+ if self.rubric_max_iterations is not None
369
+ else None
370
+ ),
360
371
  "SANDBOX_TYPE": self.sandbox_type,
361
372
  "SANDBOX_ID": self.sandbox_id,
362
373
  "SANDBOX_SNAPSHOT_NAME": self.sandbox_snapshot_name,
@@ -406,7 +417,7 @@ class ServerConfig:
406
417
  "INTERPRETER_PTC_ACKNOWLEDGE_UNSAFE"
407
418
  ),
408
419
  rubric_model=_read_env_str("RUBRIC_MODEL") or None,
409
- rubric_max_iterations=_read_env_int("RUBRIC_MAX_ITERATIONS", default=3),
420
+ rubric_max_iterations=_read_env_int("RUBRIC_MAX_ITERATIONS", default=None),
410
421
  sandbox_type=_read_env_str("SANDBOX_TYPE"),
411
422
  sandbox_id=_read_env_str("SANDBOX_ID"),
412
423
  sandbox_snapshot_name=_read_env_str("SANDBOX_SNAPSHOT_NAME") or None,
@@ -443,7 +454,7 @@ class ServerConfig:
443
454
  interpreter_ptc: str | list[str] | None = None,
444
455
  interpreter_ptc_acknowledge_unsafe: bool = False,
445
456
  rubric_model: str | None = None,
446
- rubric_max_iterations: int = 3,
457
+ rubric_max_iterations: int | None = None,
447
458
  mcp_config_path: str | None,
448
459
  no_mcp: bool,
449
460
  trust_project_mcp: bool | None,
@@ -478,7 +489,8 @@ class ServerConfig:
478
489
  interpreter_ptc_acknowledge_unsafe: Mirror of
479
490
  `settings.interpreter_ptc_acknowledge_unsafe`.
480
491
  rubric_model: Grader model spec; `None` reuses the main model.
481
- rubric_max_iterations: Grader iterations per rubric attempt.
492
+ rubric_max_iterations: Explicit grader iterations per rubric attempt;
493
+ `None` uses the SDK default.
482
494
  mcp_config_path: Path to MCP config.
483
495
  no_mcp: Disable MCP.
484
496
  trust_project_mcp: Trust project MCP servers.
@@ -0,0 +1,317 @@
1
+ """Internal chat models used by local integration tests."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
8
+ from langchain_core.messages import AIMessage, BaseMessage
9
+ from langchain_core.outputs import ChatGeneration, ChatResult
10
+ from pydantic import Field
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import Callable, Sequence
14
+
15
+ from langchain_core.callbacks import CallbackManagerForLLMRun
16
+ from langchain_core.language_models import LanguageModelInput
17
+ from langchain_core.runnables import Runnable
18
+ from langchain_core.tools import BaseTool
19
+
20
+
21
+ # Prompt markers that drive `ToolCallingIntegrationChatModel`. Each marker is the
22
+ # full token (including the trailing `=`); the file path follows on the same line,
23
+ # e.g. `DCA_TEST_WRITE_FILE=/tmp/out.txt`. These are the single source of truth
24
+ # shared with the integration tests, so the model and tests cannot drift apart.
25
+ DCA_TEST_WRITE_FILE_MARKER = "DCA_TEST_WRITE_FILE="
26
+ DCA_TEST_DELEGATE_WRITE_MARKER = "DCA_TEST_DELEGATE_WRITE="
27
+ DCA_SUBAGENT_WRITE_FILE_MARKER = "DCA_SUBAGENT_WRITE_FILE="
28
+
29
+ # Distinct file contents per write path, so a test asserting on file content can
30
+ # prove which branch executed — in particular that subagent mode delegated through
31
+ # the `task` tool rather than writing directly.
32
+ TOP_LEVEL_WRITE_CONTENT = "auto-approved"
33
+ SUBAGENT_WRITE_CONTENT = "auto-approved-subagent"
34
+
35
+
36
+ class DeterministicIntegrationChatModel(GenericFakeChatModel):
37
+ """Deterministic chat model for integration tests.
38
+
39
+ This subclasses LangChain's `GenericFakeChatModel` so the implementation
40
+ stays aligned with the core fake-chat-model test surface, while overriding
41
+ generation to remain prompt-driven and restart-safe for real CLI server
42
+ integration tests.
43
+
44
+ Why the existing `langchain_core` fakes cannot be reused here:
45
+
46
+ 1. Every core fake (`GenericFakeChatModel`, `FakeListChatModel`,
47
+ `FakeMessagesListChatModel`) pops from an iterator or cycles an index —
48
+ the actual prompt is ignored. App integration tests start and stop the
49
+ server process, which resets in-memory state. An iterator-based model
50
+ either raises `StopIteration` or replays from the beginning after a
51
+ restart, producing wrong or missing responses. This model derives output
52
+ solely from the prompt text, so identical input always produces
53
+ identical output regardless of process lifecycle.
54
+
55
+ 2. The agent runtime calls `model.bind_tools(schemas)` during
56
+ initialization. None of the core fakes implement `bind_tools`, so they
57
+ raise `AttributeError` in any agent-loop context. This model provides a
58
+ no-op passthrough.
59
+
60
+ 3. The app server reads `model.profile` for capability negotiation (e.g.
61
+ `tool_calling`, `max_input_tokens`). Core fakes have no such attribute,
62
+ causing `AttributeError` or silent misconfiguration at runtime.
63
+
64
+ Additionally, the compact middleware issues summarization prompts mid-
65
+ conversation. A list-based model cannot distinguish these from normal user
66
+ turns without pre-knowledge of exact call ordering, whereas this model
67
+ detects summary requests by inspecting the prompt content.
68
+ """
69
+
70
+ model: str = "fake"
71
+ # Required by `GenericFakeChatModel`, but our override does not consume it.
72
+ messages: object = Field(default_factory=lambda: iter(()))
73
+ profile: dict[str, Any] | None = Field(
74
+ default_factory=lambda: {
75
+ "tool_calling": True,
76
+ "max_input_tokens": 8000,
77
+ }
78
+ )
79
+
80
+ def bind_tools(
81
+ self,
82
+ tools: Sequence[dict[str, Any] | type | Callable | BaseTool], # noqa: ARG002
83
+ *,
84
+ tool_choice: str | None = None, # noqa: ARG002
85
+ **kwargs: Any, # noqa: ARG002
86
+ ) -> Runnable[LanguageModelInput, AIMessage]:
87
+ """Return self so the agent can bind tool schemas during tests."""
88
+ return self
89
+
90
+ def _generate(
91
+ self,
92
+ messages: list[BaseMessage],
93
+ stop: list[str] | None = None, # noqa: ARG002
94
+ run_manager: CallbackManagerForLLMRun | None = None, # noqa: ARG002
95
+ **kwargs: Any, # noqa: ARG002
96
+ ) -> ChatResult:
97
+ """Produce a deterministic reply derived from the prompt text.
98
+
99
+ Returns:
100
+ A single-message `ChatResult` with deterministic content.
101
+ """
102
+ prompt = "\n".join(
103
+ text
104
+ for message in messages
105
+ if (text := self._stringify_message(message)).strip()
106
+ )
107
+ if self._looks_like_summary_request(prompt):
108
+ content = "integration summary"
109
+ else:
110
+ excerpt = " ".join(prompt.split()[-18:])
111
+ if excerpt:
112
+ content = f"integration reply: {excerpt}"
113
+ else:
114
+ content = "integration reply"
115
+
116
+ return ChatResult(
117
+ generations=[ChatGeneration(message=AIMessage(content=content))]
118
+ )
119
+
120
+ @property
121
+ def _llm_type(self) -> str:
122
+ """LangChain model type identifier."""
123
+ return "deterministic-integration"
124
+
125
+ @staticmethod
126
+ def _stringify_message(message: BaseMessage) -> str:
127
+ """Flatten message content into plain text for deterministic responses.
128
+
129
+ Returns:
130
+ Plain-text content extracted from the message.
131
+ """
132
+ content = message.content
133
+ if isinstance(content, str):
134
+ return content
135
+ if isinstance(content, list):
136
+ parts: list[str] = []
137
+ for block in content:
138
+ if isinstance(block, str):
139
+ parts.append(block)
140
+ elif isinstance(block, dict) and block.get("type") == "text":
141
+ text = block.get("text")
142
+ if isinstance(text, str):
143
+ parts.append(text)
144
+ return " ".join(parts)
145
+ return str(content)
146
+
147
+ @staticmethod
148
+ def _looks_like_summary_request(prompt: str) -> bool:
149
+ """Detect the middleware's summary-generation prompt.
150
+
151
+ Returns:
152
+ `True` when the prompt appears to be a summarization request.
153
+ """
154
+ lowered = prompt.lower()
155
+ return (
156
+ "messages to summarize" in lowered
157
+ or "condense the following conversation" in lowered
158
+ or "<summary>" in lowered
159
+ )
160
+
161
+
162
+ def _extract_marker_path(prompt: str, marker: str) -> str:
163
+ """Extract the file path that follows a prompt marker on the same line.
164
+
165
+ Args:
166
+ prompt: The flattened prompt text.
167
+ marker: The marker token (including its trailing `=`) to locate.
168
+
169
+ Returns:
170
+ The stripped file path immediately following the marker.
171
+
172
+ Raises:
173
+ ValueError: If the marker is present but not followed by a path, so a
174
+ malformed test prompt fails loudly here instead of silently
175
+ degrading to a `"done"` reply or raising an opaque `IndexError`.
176
+ """
177
+ _, _, tail = prompt.partition(marker)
178
+ lines = tail.splitlines()
179
+ file_path = lines[0].strip() if lines else ""
180
+ if not file_path:
181
+ msg = (
182
+ f"Test model saw marker {marker!r} but found no file path after it; "
183
+ f"check the integration-test prompt construction."
184
+ )
185
+ raise ValueError(msg)
186
+ return file_path
187
+
188
+
189
+ def _tool_call_result(name: str, args: dict[str, Any], call_id: str) -> ChatResult:
190
+ """Build a single-tool-call `ChatResult`.
191
+
192
+ Returns:
193
+ A `ChatResult` wrapping an `AIMessage` with exactly one tool call.
194
+ """
195
+ return ChatResult(
196
+ generations=[
197
+ ChatGeneration(
198
+ message=AIMessage(
199
+ content="",
200
+ tool_calls=[
201
+ {
202
+ "name": name,
203
+ "args": args,
204
+ "id": call_id,
205
+ "type": "tool_call",
206
+ }
207
+ ],
208
+ )
209
+ )
210
+ ]
211
+ )
212
+
213
+
214
+ class ToolCallingIntegrationChatModel(DeterministicIntegrationChatModel):
215
+ """Deterministic tool-calling model for auto-approve integration tests.
216
+
217
+ Generation is driven entirely by prompt markers (the module-level `DCA_*`
218
+ constants), so output is restart-safe and independent of call ordering — the
219
+ same rationale as the parent `DeterministicIntegrationChatModel`:
220
+
221
+ - `DCA_TEST_WRITE_FILE=<path>` emits a top-level `write_file` call.
222
+ - `DCA_TEST_DELEGATE_WRITE=<path>` emits a `task` call delegating to the
223
+ `general-purpose` subagent, whose prompt then carries
224
+ `DCA_SUBAGENT_WRITE_FILE=<path>` to trigger the subagent's `write_file`.
225
+ - `DCA_SUBAGENT_WRITE_FILE=<path>` emits the subagent's `write_file` call.
226
+
227
+ Each marker fires only on the agent's first turn (no prior `ToolMessage`),
228
+ so once the tool result returns the model replies with a plain `"done"` and
229
+ the agent loop terminates instead of re-issuing the tool call.
230
+ """
231
+
232
+ # Only `_generate` is overridden; the inherited `_stream` would bypass this
233
+ # marker dispatch entirely, so streaming must be disabled.
234
+ disable_streaming: bool = True
235
+
236
+ def _generate(
237
+ self,
238
+ messages: list[BaseMessage],
239
+ stop: list[str] | None = None, # noqa: ARG002
240
+ run_manager: CallbackManagerForLLMRun | None = None, # noqa: ARG002
241
+ **kwargs: Any, # noqa: ARG002
242
+ ) -> ChatResult:
243
+ """Emit a deterministic tool call (or terminal reply) from prompt markers.
244
+
245
+ The `has_tool_result` guard ensures each marker fires only on the
246
+ agent's first turn; once a `ToolMessage` is present the model returns
247
+ `"done"` so the agent loop terminates.
248
+
249
+ A recognized marker with no file path raises `ValueError` (via
250
+ `_extract_marker_path`) rather than degrading to `"done"`.
251
+
252
+ Returns:
253
+ A single-message `ChatResult`: an `AIMessage` carrying a `task` or
254
+ `write_file` tool call when a marker matches and no tool result is
255
+ present yet, otherwise a plain `"done"` reply.
256
+ """
257
+ prompt = "\n".join(
258
+ text
259
+ for message in messages
260
+ if (text := self._stringify_message(message)).strip()
261
+ )
262
+ has_tool_result = any(message.type == "tool" for message in messages)
263
+ if not has_tool_result:
264
+ for marker, build_tool_call in self._marker_dispatch():
265
+ if marker in prompt:
266
+ name, args, call_id = build_tool_call(
267
+ _extract_marker_path(prompt, marker)
268
+ )
269
+ return _tool_call_result(name, args, call_id)
270
+
271
+ return ChatResult(
272
+ generations=[ChatGeneration(message=AIMessage(content="done"))]
273
+ )
274
+
275
+ @staticmethod
276
+ def _marker_dispatch() -> tuple[
277
+ tuple[str, Callable[[str], tuple[str, dict[str, Any], str]]], ...
278
+ ]:
279
+ """Return ordered `(marker, tool-call builder)` pairs.
280
+
281
+ The delegate marker is checked before the plain write markers because
282
+ its emitted `task` description embeds `DCA_SUBAGENT_WRITE_FILE=`; the
283
+ first marker found in the prompt wins.
284
+
285
+ Returns:
286
+ Marker-to-builder pairs in precedence order. Each builder maps an
287
+ extracted file path to a `(tool_name, args, call_id)` triple.
288
+ """
289
+ return (
290
+ (
291
+ DCA_TEST_DELEGATE_WRITE_MARKER,
292
+ lambda path: (
293
+ "task",
294
+ {
295
+ "description": f"{DCA_SUBAGENT_WRITE_FILE_MARKER}{path}",
296
+ "subagent_type": "general-purpose",
297
+ },
298
+ "call_task",
299
+ ),
300
+ ),
301
+ (
302
+ DCA_SUBAGENT_WRITE_FILE_MARKER,
303
+ lambda path: (
304
+ "write_file",
305
+ {"file_path": path, "content": SUBAGENT_WRITE_CONTENT},
306
+ "call_write_file",
307
+ ),
308
+ ),
309
+ (
310
+ DCA_TEST_WRITE_FILE_MARKER,
311
+ lambda path: (
312
+ "write_file",
313
+ {"file_path": path, "content": TOP_LEVEL_WRITE_CONTENT},
314
+ "call_write_file",
315
+ ),
316
+ ),
317
+ )
@@ -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.28" # x-release-please-version
5
+ __version__ = "0.1.29" # 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."""
@@ -10,13 +10,19 @@ import shutil
10
10
  import tempfile
11
11
  import tomllib
12
12
  import warnings
13
- from pathlib import Path
13
+ from pathlib import Path, PurePosixPath
14
14
  from typing import TYPE_CHECKING, Any, cast
15
15
 
16
16
  from deepagents import create_deep_agent
17
17
  from deepagents.backends import CompositeBackend, LocalShellBackend
18
18
  from deepagents.backends.filesystem import FilesystemBackend
19
- from deepagents.middleware import MemoryMiddleware, RubricMiddleware, SkillsMiddleware
19
+ from deepagents.middleware import (
20
+ GRADER_SYSTEM_PROMPT,
21
+ FilesystemMiddleware,
22
+ MemoryMiddleware,
23
+ RubricMiddleware,
24
+ SkillsMiddleware,
25
+ )
20
26
 
21
27
  # Backwards-compat flag: SDKs before 0.5.4 accept only `list[str]` for
22
28
  # `SkillsMiddleware.sources`; newer SDKs expose the `SkillSource` alias
@@ -52,6 +58,10 @@ if TYPE_CHECKING:
52
58
  from deepagents_code.output import OutputFormat
53
59
 
54
60
  from langchain.agents.middleware.types import AgentMiddleware
61
+ from langchain.tools import (
62
+ ToolRuntime, # noqa: TC002 # LangChain inspects this annotation for runtime injection.
63
+ )
64
+ from langchain_core.tools import StructuredTool, tool
55
65
 
56
66
  from deepagents_code import theme
57
67
  from deepagents_code._cli_context import CLIContextSchema
@@ -90,6 +100,66 @@ logger = logging.getLogger(__name__)
90
100
  REQUIRE_COMPACT_TOOL_APPROVAL: bool = True
91
101
  """When `True`, `compact_conversation` requires HITL approval like other gated tools."""
92
102
 
103
+ _RUBRIC_GRADER_READ_FILE_PREFIX = "/large_tool_results/"
104
+ _RUBRIC_GRADER_SYSTEM_PROMPT = (
105
+ GRADER_SYSTEM_PROMPT
106
+ + "\n\nWhen the transcript says a tool result was saved under "
107
+ + f"`{_RUBRIC_GRADER_READ_FILE_PREFIX}`, use the `read_file` tool to inspect "
108
+ + "the referenced evidence before deciding that a criterion lacks support. "
109
+ + "Only read paths that are explicitly present in the transcript."
110
+ )
111
+
112
+
113
+ def _validate_rubric_grader_read_path(file_path: str) -> str | None:
114
+ normalized = file_path.replace("\\", "/")
115
+ if not normalized.startswith(_RUBRIC_GRADER_READ_FILE_PREFIX):
116
+ return "Rubric grader can only read files under /large_tool_results/."
117
+ parts = PurePosixPath(normalized).parts
118
+ if ".." in parts or "~" in parts:
119
+ return "Invalid path."
120
+ return None
121
+
122
+
123
+ def _create_rubric_grader_tools(backend: CompositeBackend) -> list[BaseTool]:
124
+ filesystem = FilesystemMiddleware(backend=backend)
125
+ sdk_read_file: StructuredTool | None = None
126
+ for candidate in filesystem.tools:
127
+ if candidate.name == "read_file":
128
+ sdk_read_file = cast("StructuredTool", candidate)
129
+ break
130
+ if sdk_read_file is None:
131
+ msg = "SDK read_file tool is unavailable."
132
+ raise RuntimeError(msg)
133
+
134
+ sdk_read_file_func = sdk_read_file.func
135
+ if sdk_read_file_func is None:
136
+ msg = "SDK read_file tool is missing a sync implementation."
137
+ raise RuntimeError(msg)
138
+
139
+ @tool(description=sdk_read_file.description)
140
+ def read_file(
141
+ file_path: str,
142
+ runtime: ToolRuntime[None, Any],
143
+ offset: int = 0,
144
+ limit: int = 100,
145
+ ) -> object:
146
+ """Read an offloaded tool result referenced in the transcript.
147
+
148
+ Returns:
149
+ The SDK `read_file` tool result, or an error message when the path is
150
+ outside the grader evidence directory.
151
+ """
152
+ if error := _validate_rubric_grader_read_path(file_path):
153
+ return error
154
+ return sdk_read_file_func(
155
+ file_path=file_path,
156
+ runtime=runtime,
157
+ offset=offset,
158
+ limit=limit,
159
+ )
160
+
161
+ return [read_file]
162
+
93
163
 
94
164
  def _sanitize_agent_message_name(agent_name: str) -> str:
95
165
  """Return a provider-safe message name for a user-facing agent name.
@@ -275,17 +345,17 @@ def _resolve_ptc_option(
275
345
  return None
276
346
 
277
347
  live_names: list[str] = []
278
- for tool in tools:
279
- if isinstance(tool, _BaseTool):
280
- name = tool.name
348
+ for candidate in tools:
349
+ if isinstance(candidate, _BaseTool):
350
+ name = candidate.name
281
351
  if isinstance(name, str):
282
352
  live_names.append(name)
283
- elif isinstance(tool, dict):
284
- raw_name = cast("dict[str, Any]", tool).get("name")
353
+ elif isinstance(candidate, dict):
354
+ raw_name = cast("dict[str, Any]", candidate).get("name")
285
355
  if isinstance(raw_name, str):
286
356
  live_names.append(raw_name)
287
357
  else:
288
- attr = getattr(tool, "name", None)
358
+ attr = getattr(candidate, "name", None)
289
359
  if isinstance(attr, str):
290
360
  live_names.append(attr)
291
361
  live_set: set[str] = set(live_names)
@@ -1251,7 +1321,7 @@ def create_cli_agent(
1251
1321
  enable_shell: bool = True,
1252
1322
  enable_interpreter: bool = False,
1253
1323
  rubric_model: str | BaseChatModel | None = None,
1254
- rubric_max_iterations: int = 3,
1324
+ rubric_max_iterations: int | None = None,
1255
1325
  checkpointer: BaseCheckpointSaver | None = None,
1256
1326
  mcp_server_info: list[MCPServerInfo] | None = None,
1257
1327
  cwd: str | Path | None = None,
@@ -1333,8 +1403,9 @@ def create_cli_agent(
1333
1403
  A `'provider:model'` string or `BaseChatModel`.
1334
1404
 
1335
1405
  When `None`, the main `model` is reused.
1336
- rubric_max_iterations: Grader iterations per rubric attempt before the
1337
- agent terminates with `'max_iterations_reached'`.
1406
+ rubric_max_iterations: Explicit grader iterations per rubric attempt
1407
+ before the agent terminates with `'max_iterations_reached'`; `None`
1408
+ uses the SDK default.
1338
1409
  checkpointer: Optional checkpointer for session persistence.
1339
1410
  When `None`, the graph is compiled without a checkpointer.
1340
1411
  mcp_server_info: MCP server metadata to surface in the system prompt.
@@ -1728,12 +1799,14 @@ def create_cli_agent(
1728
1799
  message="The middleware `RubricMiddleware` is in beta",
1729
1800
  category=Warning,
1730
1801
  )
1731
- agent_middleware.append(
1732
- RubricMiddleware(
1733
- model=rubric_model if rubric_model is not None else model,
1734
- max_iterations=rubric_max_iterations,
1735
- )
1736
- )
1802
+ rubric_kwargs: dict[str, Any] = {
1803
+ "model": rubric_model if rubric_model is not None else model,
1804
+ "system_prompt": _RUBRIC_GRADER_SYSTEM_PROMPT,
1805
+ "tools": _create_rubric_grader_tools(composite_backend),
1806
+ }
1807
+ if rubric_max_iterations is not None:
1808
+ rubric_kwargs["max_iterations"] = rubric_max_iterations
1809
+ agent_middleware.append(RubricMiddleware(**rubric_kwargs))
1737
1810
 
1738
1811
  # Create the agent
1739
1812
  all_subagents: list[SubAgent | CompiledSubAgent | AsyncSubAgent] = [