deepagents-code 0.1.49__tar.gz → 0.1.50__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 (462) hide show
  1. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/.gitignore +10 -0
  2. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/AGENTS.md +15 -1
  3. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/ARCHITECTURE.md +1 -0
  4. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/CHANGELOG.md +24 -0
  5. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/COMMANDS.md +1 -1
  6. deepagents_code-0.1.50/HOOKS.md +121 -0
  7. deepagents_code-0.1.50/LICENSE +21 -0
  8. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/Makefile +3 -3
  9. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/PKG-INFO +6 -5
  10. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/THREAT_MODEL.md +6 -5
  11. deepagents_code-0.1.50/deepagents_code/_ask_user_types.py +257 -0
  12. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_build_info.py +1 -1
  13. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_cli_context.py +23 -0
  14. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_env_vars.py +11 -0
  15. deepagents_code-0.1.50/deepagents_code/_invocation.py +133 -0
  16. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_textual_patches.py +53 -1
  17. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_tool_stream.py +7 -0
  18. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_version.py +1 -1
  19. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/agent.py +57 -17
  20. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/app.py +1270 -234
  21. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/approval_mode.py +11 -0
  22. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/ask_user.py +100 -34
  23. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/auto_mode.py +269 -77
  24. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/commands/config.py +7 -1
  25. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/non_interactive.py +483 -38
  26. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/clipboard.py +13 -5
  27. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/command_registry.py +2 -2
  28. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/config_manifest.py +7 -1
  29. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/capabilities.py +1 -1
  30. deepagents_code-0.1.50/deepagents_code/hooks/client.py +152 -0
  31. deepagents_code-0.1.50/deepagents_code/hooks/client_lifecycle.py +417 -0
  32. deepagents_code-0.1.50/deepagents_code/hooks/context.py +38 -0
  33. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/engine.py +78 -16
  34. deepagents_code-0.1.50/deepagents_code/hooks/envelope.py +78 -0
  35. deepagents_code-0.1.50/deepagents_code/hooks/interrupt.py +121 -0
  36. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/loading.py +35 -19
  37. deepagents_code-0.1.50/deepagents_code/hooks/manager.py +626 -0
  38. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/migration.py +2 -3
  39. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/models/domain.py +58 -10
  40. deepagents_code-0.1.50/deepagents_code/hooks/permissions.py +160 -0
  41. deepagents_code-0.1.50/deepagents_code/hooks/presenter.py +217 -0
  42. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/projection.py +25 -18
  43. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/runtime.py +70 -9
  44. deepagents_code-0.1.50/deepagents_code/hooks/server_middleware.py +1108 -0
  45. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/snapshot.py +15 -2
  46. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/transcript.py +173 -11
  47. deepagents_code-0.1.50/deepagents_code/hooks/trust.py +470 -0
  48. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/input.py +71 -3
  49. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/main.py +241 -75
  50. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/media_utils.py +23 -0
  51. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/model_config.py +15 -3
  52. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/sessions.py +7 -1
  53. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/textual_adapter.py +684 -79
  54. deepagents_code-0.1.50/deepagents_code/tui/widgets/_copy_spans.py +52 -0
  55. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/_inline_prompt.py +127 -1
  56. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/ask_user.py +62 -3
  57. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/chat_input.py +9 -7
  58. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/debug_console.py +86 -52
  59. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/messages.py +315 -8
  60. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/notification_settings.py +11 -3
  61. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/status.py +27 -5
  62. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/welcome.py +74 -8
  63. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/ui.py +3 -0
  64. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/pyproject.toml +4 -4
  65. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/conftest.py +124 -2
  66. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/hooks/models/test_models.py +5 -7
  67. deepagents_code-0.1.50/tests/unit_tests/hooks/test_client_lifecycle.py +187 -0
  68. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/hooks/test_configuration.py +8 -1
  69. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/hooks/test_engine.py +98 -21
  70. deepagents_code-0.1.50/tests/unit_tests/hooks/test_manager.py +126 -0
  71. deepagents_code-0.1.50/tests/unit_tests/hooks/test_presenter.py +80 -0
  72. deepagents_code-0.1.50/tests/unit_tests/hooks/test_server_lifecycle.py +1292 -0
  73. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/hooks/test_transcript.py +97 -5
  74. deepagents_code-0.1.50/tests/unit_tests/hooks/test_trust.py +383 -0
  75. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_agent.py +60 -1
  76. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_app.py +884 -39
  77. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_ask_user_middleware.py +143 -29
  78. deepagents_code-0.1.50/tests/unit_tests/test_ask_user_types.py +118 -0
  79. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_auto_mode.py +526 -0
  80. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_clipboard.py +140 -14
  81. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_debug_console.py +268 -12
  82. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_doctor.py +9 -6
  83. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_input_parsing.py +190 -0
  84. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_install_command.py +143 -4
  85. deepagents_code-0.1.50/tests/unit_tests/test_invocation.py +270 -0
  86. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_main.py +98 -63
  87. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_media_utils.py +19 -0
  88. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_model_config.py +23 -0
  89. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_non_interactive.py +440 -0
  90. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_offload.py +104 -30
  91. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_startup_fast_paths.py +3 -2
  92. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_textual_patches.py +48 -0
  93. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_version.py +333 -10
  94. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/test_textual_adapter.py +1197 -170
  95. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_ask_user.py +239 -2
  96. deepagents_code-0.1.50/tests/unit_tests/tui/widgets/test_copy_spans.py +43 -0
  97. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_goal_review.py +6 -2
  98. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_inline_prompt.py +343 -18
  99. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_messages.py +605 -4
  100. deepagents_code-0.1.50/tests/unit_tests/tui/widgets/test_notification_settings.py +140 -0
  101. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_status.py +16 -0
  102. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_thread_selector.py +226 -26
  103. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_welcome.py +154 -0
  104. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/uv.lock +32 -32
  105. deepagents_code-0.1.49/deepagents_code/_ask_user_types.py +0 -104
  106. deepagents_code-0.1.49/deepagents_code/todo_list_prompt.md +0 -12
  107. deepagents_code-0.1.49/tests/unit_tests/tui/widgets/test_notification_settings.py +0 -29
  108. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/DEVELOPMENT.md +0 -0
  109. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/README.md +0 -0
  110. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/__init__.py +0 -0
  111. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/__main__.py +0 -0
  112. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_constants.py +0 -0
  113. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_debug.py +0 -0
  114. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_debug_buffer.py +0 -0
  115. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_fake_models.py +0 -0
  116. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_git.py +0 -0
  117. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_glm_5p2_profile.py +0 -0
  118. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_paths.py +0 -0
  119. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_repository_bounds.py +0 -0
  120. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_server_config.py +0 -0
  121. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_session_stats.py +0 -0
  122. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_startup_error.py +0 -0
  123. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/_testing_models.py +0 -0
  124. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/app.tcss +0 -0
  125. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/auth_display.py +0 -0
  126. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/auth_store.py +0 -0
  127. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/built_in_skills/__init__.py +0 -0
  128. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/built_in_skills/deepagents-thread-inspector/SKILL.md +0 -0
  129. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/built_in_skills/deepagents-thread-inspector/scripts/inspect_sessions.py +0 -0
  130. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
  131. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
  132. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  133. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  134. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/__init__.py +0 -0
  135. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/commands/__init__.py +0 -0
  136. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/commands/auth.py +0 -0
  137. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/commands/extras.py +0 -0
  138. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/commands/mcp.py +0 -0
  139. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/commands/tools.py +0 -0
  140. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/launch/__init__.py +0 -0
  141. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/launch/server.py +0 -0
  142. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/launch/server_manager.py +0 -0
  143. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/client/remote_client.py +0 -0
  144. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/config.py +0 -0
  145. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/configurable_model.py +0 -0
  146. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/default_agent_prompt.md +0 -0
  147. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/doctor.py +0 -0
  148. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/editor.py +0 -0
  149. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/event_bus.py +0 -0
  150. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/extras_info.py +0 -0
  151. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/file_ops.py +0 -0
  152. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/formatting.py +0 -0
  153. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/goal_rubric.py +0 -0
  154. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/goal_state_notice.py +0 -0
  155. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/goal_tools.py +0 -0
  156. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/__init__.py +0 -0
  157. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/env.py +0 -0
  158. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/legacy.py +0 -0
  159. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/models/__init__.py +0 -0
  160. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/models/adapters.py +0 -0
  161. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/models/config.py +0 -0
  162. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/models/transport.py +0 -0
  163. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/models/wire.py +0 -0
  164. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/reducer.py +0 -0
  165. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/runner.py +0 -0
  166. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/tools.py +0 -0
  167. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/hooks/validate_terminal_sequence.py +0 -0
  168. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/integrations/__init__.py +0 -0
  169. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/integrations/openai_codex.py +0 -0
  170. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/integrations/sandbox_config.py +0 -0
  171. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/integrations/sandbox_factory.py +0 -0
  172. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/integrations/sandbox_provider.py +0 -0
  173. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/integrations/sandbox_registry.py +0 -0
  174. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/iterm_cursor_guide.py +0 -0
  175. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/json_types.py +0 -0
  176. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/local_context.py +0 -0
  177. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/managed_tools.py +0 -0
  178. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_auth.py +0 -0
  179. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_config.py +0 -0
  180. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_disabled.py +0 -0
  181. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_login_service.py +0 -0
  182. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_oauth_ui.py +0 -0
  183. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_providers/__init__.py +0 -0
  184. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_providers/_registry.py +0 -0
  185. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_providers/base.py +0 -0
  186. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_providers/github.py +0 -0
  187. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_providers/slack.py +0 -0
  188. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/mcp_tools.py +0 -0
  189. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/memory_guard.py +0 -0
  190. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/notifications.py +0 -0
  191. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/offload.py +0 -0
  192. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/offload_middleware.py +0 -0
  193. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/onboarding.py +0 -0
  194. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/output.py +0 -0
  195. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/paste_collapse.py +0 -0
  196. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/__init__.py +0 -0
  197. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/_json.py +0 -0
  198. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/adapters/__init__.py +0 -0
  199. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/adapters/mcp.py +0 -0
  200. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/adapters/skills.py +0 -0
  201. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/adapters/skills_middleware.py +0 -0
  202. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/commands_cli.py +0 -0
  203. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/discovery.py +0 -0
  204. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/manifest.py +0 -0
  205. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/marketplace.py +0 -0
  206. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/models.py +0 -0
  207. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/store.py +0 -0
  208. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/plugins/substitution.py +0 -0
  209. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/project_utils.py +0 -0
  210. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/py.typed +0 -0
  211. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/reasoning_effort.py +0 -0
  212. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/reliable_rubric.py +0 -0
  213. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/resume_state.py +0 -0
  214. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/server_graph.py +0 -0
  215. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/skills/__init__.py +0 -0
  216. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/skills/commands.py +0 -0
  217. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/skills/invocation.py +0 -0
  218. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/skills/load.py +0 -0
  219. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/skills/merge.py +0 -0
  220. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/skills/trust.py +0 -0
  221. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/state_migration.py +0 -0
  222. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/subagents.py +0 -0
  223. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/system_prompt.md +0 -0
  224. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/terminal_capabilities.py +0 -0
  225. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/terminal_escape.py +0 -0
  226. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/theme.py +0 -0
  227. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tool_catalog.py +0 -0
  228. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tool_display.py +0 -0
  229. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tools.py +0 -0
  230. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/__init__.py +0 -0
  231. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/modals/__init__.py +0 -0
  232. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/modals/plugin_manager/__init__.py +0 -0
  233. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/modals/plugin_manager/content.py +0 -0
  234. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/modals/plugin_manager/models.py +0 -0
  235. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/modals/plugin_manager/plugin_manager.tcss +0 -0
  236. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/modals/plugin_manager/state.py +0 -0
  237. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/modals/plugin_manager/tabs.py +0 -0
  238. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/screens/__init__.py +0 -0
  239. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/__init__.py +0 -0
  240. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/_js_eval_display.py +0 -0
  241. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/_links.py +0 -0
  242. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/_paste_textarea.py +0 -0
  243. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/agent_selector.py +0 -0
  244. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/approval.py +0 -0
  245. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/auth.py +0 -0
  246. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/auto_mode_notice.py +0 -0
  247. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/autocomplete.py +0 -0
  248. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/codex_auth.py +0 -0
  249. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/cwd_switch.py +0 -0
  250. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/diff.py +0 -0
  251. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/effort_selector.py +0 -0
  252. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/goal_review.py +0 -0
  253. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/goal_status.py +0 -0
  254. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/history.py +0 -0
  255. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/install_confirm.py +0 -0
  256. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/launch_init.py +0 -0
  257. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/loading.py +0 -0
  258. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/mcp_login.py +0 -0
  259. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/mcp_reconnect.py +0 -0
  260. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/mcp_viewer.py +0 -0
  261. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/message_store.py +0 -0
  262. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/model_selector.py +0 -0
  263. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/notification_center.py +0 -0
  264. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/notification_detail.py +0 -0
  265. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/plugin_reload.py +0 -0
  266. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/restart_prompt.py +0 -0
  267. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/skill_trust.py +0 -0
  268. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/startup_tip.py +0 -0
  269. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/subagent_panel.py +0 -0
  270. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/theme_selector.py +0 -0
  271. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/thread_selector.py +0 -0
  272. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/tool_renderers.py +0 -0
  273. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/tool_widgets.py +0 -0
  274. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/update_available.py +0 -0
  275. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/update_confirm.py +0 -0
  276. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/update_progress.py +0 -0
  277. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/tui/widgets/yolo_mode_notice.py +0 -0
  278. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/unicode_security.py +0 -0
  279. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/deepagents_code/update_check.py +0 -0
  280. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/examples/skills/arxiv-search/SKILL.md +0 -0
  281. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  282. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/examples/skills/langgraph-docs/SKILL.md +0 -0
  283. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/examples/skills/skill-creator/SKILL.md +0 -0
  284. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  285. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  286. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/examples/skills/web-research/SKILL.md +0 -0
  287. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/hatch_build.py +0 -0
  288. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/images/tui.png +0 -0
  289. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/scripts/check_imports.py +0 -0
  290. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/scripts/debug_server.sh +0 -0
  291. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/scripts/generate_commands_catalog.py +0 -0
  292. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/scripts/install.sh +0 -0
  293. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/README.md +0 -0
  294. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/__init__.py +0 -0
  295. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/benchmarks/__init__.py +0 -0
  296. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  297. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/benchmarks/test_local_context_benchmarks.py +0 -0
  298. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  299. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/conftest.py +0 -0
  300. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/test_acp_mode.py +0 -0
  301. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/test_auto_approve_remote.py +0 -0
  302. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/test_compact_resume.py +0 -0
  303. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/test_goal_criteria_remote.py +0 -0
  304. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/test_offload_server_side.py +0 -0
  305. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/test_sandbox_factory.py +0 -0
  306. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/integration_tests/test_sandbox_operations.py +0 -0
  307. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/__init__.py +0 -0
  308. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/client/__init__.py +0 -0
  309. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/client/commands/__init__.py +0 -0
  310. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/client/commands/test_auth.py +0 -0
  311. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/client/commands/test_extras.py +0 -0
  312. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/client/commands/test_mcp.py +0 -0
  313. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/client/commands/test_tools.py +0 -0
  314. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/data/coding_agent_v1_validator.json +0 -0
  315. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/hooks/__init__.py +0 -0
  316. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/hooks/models/__init__.py +0 -0
  317. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/hooks/test_execution.py +0 -0
  318. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/plugins/__init__.py +0 -0
  319. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/plugins/test_marketplace.py +0 -0
  320. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/plugins/test_plugins_p0.py +0 -0
  321. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/plugins/test_store.py +0 -0
  322. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/skills/__init__.py +0 -0
  323. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/skills/test_commands.py +0 -0
  324. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/skills/test_load.py +0 -0
  325. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/skills/test_merge.py +0 -0
  326. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/skills/test_skill_trust.py +0 -0
  327. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/skills/test_skills_json.py +0 -0
  328. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/skills/test_thread_inspector.py +0 -0
  329. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/smoke_tests/__init__.py +0 -0
  330. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/smoke_tests/conftest.py +0 -0
  331. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/smoke_tests/snapshots/system_prompt_interactive_local.md +0 -0
  332. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/smoke_tests/test_system_prompt.py +0 -0
  333. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test__tool_stream.py +0 -0
  334. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_agent_friendly.py +0 -0
  335. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_approval_mode.py +0 -0
  336. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_args.py +0 -0
  337. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_auth_display.py +0 -0
  338. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_auth_store.py +0 -0
  339. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_auth_web_search_restart.py +0 -0
  340. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_charset.py +0 -0
  341. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_coding_agent_metadata.py +0 -0
  342. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_command_registry.py +0 -0
  343. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_compact_tool.py +0 -0
  344. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_config.py +0 -0
  345. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_config_manifest.py +0 -0
  346. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_config_recursion_limit.py +0 -0
  347. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_configurable_model.py +0 -0
  348. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_cursor_blink.py +0 -0
  349. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_cursor_style.py +0 -0
  350. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_debug.py +0 -0
  351. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_debug_buffer.py +0 -0
  352. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_editor.py +0 -0
  353. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_end_to_end.py +0 -0
  354. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_env_vars.py +0 -0
  355. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_event_bus.py +0 -0
  356. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_exception_handling.py +0 -0
  357. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_extras_info.py +0 -0
  358. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_file_ops.py +0 -0
  359. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_formatting.py +0 -0
  360. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_git.py +0 -0
  361. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_glm_5p2_profile.py +0 -0
  362. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_goal_criteria_client.py +0 -0
  363. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_goal_rubric.py +0 -0
  364. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_goal_state_notice.py +0 -0
  365. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_goal_state_persistence.py +0 -0
  366. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_goal_tools.py +0 -0
  367. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_hatch_build.py +0 -0
  368. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_hooks.py +0 -0
  369. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_imports.py +0 -0
  370. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_install_script.py +0 -0
  371. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
  372. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_json_types.py +0 -0
  373. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_local_context.py +0 -0
  374. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_main_acp_mode.py +0 -0
  375. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_main_args.py +0 -0
  376. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_managed_tools.py +0 -0
  377. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_mcp_auth.py +0 -0
  378. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_mcp_config.py +0 -0
  379. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_mcp_disabled.py +0 -0
  380. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_mcp_login_service.py +0 -0
  381. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
  382. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_mcp_tools.py +0 -0
  383. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_memory_guard.py +0 -0
  384. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_model_switch.py +0 -0
  385. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_notifications.py +0 -0
  386. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_onboarding.py +0 -0
  387. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_openai_codex_integration.py +0 -0
  388. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_output.py +0 -0
  389. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_paths.py +0 -0
  390. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_reasoning_effort.py +0 -0
  391. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_reliable_rubric.py +0 -0
  392. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_reload.py +0 -0
  393. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_remote_client.py +0 -0
  394. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_repository_bounds.py +0 -0
  395. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_resume_state.py +0 -0
  396. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_rubric_cli.py +0 -0
  397. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_sandbox_config.py +0 -0
  398. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_sandbox_factory.py +0 -0
  399. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_sandbox_provider.py +0 -0
  400. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_sandbox_registry.py +0 -0
  401. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_server.py +0 -0
  402. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_server_config.py +0 -0
  403. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_server_graph.py +0 -0
  404. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_server_helpers.py +0 -0
  405. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_server_manager.py +0 -0
  406. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_session_stats.py +0 -0
  407. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_sessions.py +0 -0
  408. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_shell_allow_list.py +0 -0
  409. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_skill_invocation.py +0 -0
  410. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_startup_blocking.py +0 -0
  411. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_state_migration.py +0 -0
  412. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_subagents.py +0 -0
  413. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  414. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_terminal_escape.py +0 -0
  415. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_terminal_progress_preference.py +0 -0
  416. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_theme.py +0 -0
  417. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_threads_resume.py +0 -0
  418. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_tool_catalog.py +0 -0
  419. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_tool_display.py +0 -0
  420. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_tracing_replicas.py +0 -0
  421. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_transcript_virtualization.py +0 -0
  422. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_ui.py +0 -0
  423. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_unicode_security.py +0 -0
  424. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/test_update_check.py +0 -0
  425. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tools/__init__.py +0 -0
  426. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tools/test_current_thread.py +0 -0
  427. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tools/test_fetch_url.py +0 -0
  428. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tools/test_tool_arg_schemas.py +0 -0
  429. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/__init__.py +0 -0
  430. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/modals/__init__.py +0 -0
  431. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/modals/plugin_manager/__init__.py +0 -0
  432. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/modals/plugin_manager/test_structure.py +0 -0
  433. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/test_subagent_stream.py +0 -0
  434. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/__init__.py +0 -0
  435. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_agent_selector.py +0 -0
  436. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_approval.py +0 -0
  437. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_auth_widgets.py +0 -0
  438. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_autocomplete.py +0 -0
  439. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_chat_input.py +0 -0
  440. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_cwd_switch.py +0 -0
  441. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_diff.py +0 -0
  442. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_history.py +0 -0
  443. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_install_confirm.py +0 -0
  444. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_launch_init.py +0 -0
  445. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_links.py +0 -0
  446. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_loading.py +0 -0
  447. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_mcp_login_modal.py +0 -0
  448. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_mcp_reconnect.py +0 -0
  449. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_mcp_viewer.py +0 -0
  450. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_message_store.py +0 -0
  451. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_model_selector.py +0 -0
  452. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_notification_center.py +0 -0
  453. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_notification_detail.py +0 -0
  454. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_plugin_reload.py +0 -0
  455. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_restart_prompt.py +0 -0
  456. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_skill_trust_screen.py +0 -0
  457. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_startup_tip.py +0 -0
  458. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_subagent_panel.py +0 -0
  459. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_tool_renderers.py +0 -0
  460. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_update_available.py +0 -0
  461. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_update_confirm.py +0 -0
  462. {deepagents_code-0.1.49 → deepagents_code-0.1.50}/tests/unit_tests/tui/widgets/test_update_progress.py +0 -0
@@ -162,6 +162,16 @@ Cookies
162
162
  Cookies.db
163
163
  cookies.sqlite
164
164
  cookies.txt
165
+ # OpenSSH private keys (no extension, so *.key never matches);
166
+ # .pub counterparts are not secrets and stay committable.
167
+ id_rsa
168
+ id_dsa
169
+ id_ecdsa
170
+ id_ed25519
171
+ *_rsa
172
+ *_dsa
173
+ *_ecdsa
174
+ *_ed25519
165
175
 
166
176
  # Spyder project settings
167
177
  .spyderproject
@@ -30,6 +30,15 @@ IMPORTANT: `Content` requires **Textual's** `Style` (`textual.style.Style`) for
30
30
 
31
31
  **Decision rule:** if the value could ever come from outside the codebase (user input, tool output, API responses, file contents), use `from_markup` with `$var`. If it's a hardcoded string, glyph, or computed int, `styled` is fine.
32
32
 
33
+ ### Markdown messages take a different escape path
34
+
35
+ The above applies to Rich **markup**. A message mounted as markdown — `AppMessage(source, markdown=True)`, used by `/version` and `/tools` — is parsed as markdown instead, so `from_markup`/`$var` does not apply and offers no protection there. Build the source with the helpers in `app.py`:
36
+
37
+ - `_escape_markdown(text)` — backslash-escapes markdown punctuation **and** collapses line breaks to spaces. Both matter: unescaped punctuation is parsed (`<b>x</b>` is swallowed as HTML, `[d](u)` collapses to its link text), and an embedded newline ends the current block, so external text could otherwise inject its own headings, lists, and tables.
38
+ - `_markdown_table(headers, rows)` — builds a pipe table, escaping every header and cell, so a `|` cannot forge an extra column.
39
+
40
+ **Decision rule:** any string reaching markdown source from outside the codebase goes through `_escape_markdown` (or `_markdown_table`, which applies it per cell). Only internal literals and computed ints may be interpolated raw.
41
+
33
42
  ### `App.notify()` defaults to `markup=True`
34
43
 
35
44
  Textual's `App.notify(message)` parses the message string as Rich markup by default. Any dynamic content (exception messages, file paths, user input, command strings) containing square brackets is unsafe: tag-shaped text like `[bold]` is silently swallowed, and closing-tag-shaped text like `[/tmp/x]` raises a `MarkupError` crash in Textual's Toast renderer. Always pass `markup=False` when the message contains f-string interpolated variables. Hardcoded string literals are safe with the default.
@@ -70,7 +79,8 @@ Apply these rules to new UI; do not treat them as a mandate to refactor existing
70
79
  - Snapshot testing available for visual regression - see repo `notes/snapshot_testing.md`
71
80
  - For modal flows, test the real interaction path with keypresses when possible. Unit tests that call action methods or resume handlers directly can miss focus and modal-stack bugs.
72
81
  - Do not open another modal or refocus the base chat input directly inside a modal dismiss callback. Preserve the non-blocking `push_screen(..., callback)` flow and schedule follow-up UI work with `call_after_refresh` so the dismissing modal fully unwinds first.
73
- - Be cautious replacing `push_screen(..., callback)` with an awaited modal result inside slash-command handlers; awaiting can block the Textual message pump and break keyboard navigation in the active modal.
82
+ - Be cautious replacing `push_screen(..., callback)` with an awaited modal result inside slash-command handlers; awaiting can block the Textual message pump and break keyboard navigation in the active modal. Slash commands are dispatched from `on_chat_input_submitted`, which is awaited inline on that pump, so a modal awaited anywhere in the call chain never receives the keys that would resolve it and looks frozen.
83
+ - When a command genuinely needs an awaited modal result, hand the continuation to `_schedule_off_message_pump` so the handler returns first. A detached continuation runs outside the handler's `try/except` and is cancelled at app exit, so it must catch its own exceptions and mount its own failure message — otherwise the failure reaches only `_log_task_exception` and the user sees no outcome at all — and it must tolerate cancellation part-way through, including mid-subprocess. Anything that mutates the running tool environment must also take `_environment_mutation_lock`, since the handler no longer serializes commands by blocking.
74
84
 
75
85
  ### Typing and test doubles
76
86
 
@@ -122,6 +132,10 @@ Debug logging is configured **once**, on the `deepagents_code` package logger, b
122
132
 
123
133
  The `deepagents-code --help` screen is hand-maintained in `ui.show_help()`, separate from the argparse definitions in `main.parse_args()`. When adding a new CLI flag, update **both** files. A drift-detection test (`test_args.TestHelpScreenDrift`) fails if a flag is registered in argparse but missing from the help screen.
124
134
 
135
+ ## Command name in hints
136
+
137
+ Hints that tell the user to run something (for example the teardown `-r <thread>` resume hint) must render `_invocation.invoked_name()` rather than a literal `dcode`. The package ships two console scripts (`dcode` and `deepagents-code`), and per-checkout shims — a renamed symlink pointing at a worktree's `bin/dcode` — are a common way to run several installs side by side, so a hardcoded name sends those users to a command they do not have. `invoked_name()` resolves from `sys.argv[0]` (an internal env sentinel carries it across the auto-update re-exec, which drops argv[0]) and falls back to `dcode` when the launch name is not a plausible command. The hand-maintained `--help` usage lines still hardcode `dcode`.
138
+
125
139
  ## Splash screen tips
126
140
 
127
141
  When adding a user-facing CLI feature (new slash command, keybinding, workflow), add a corresponding entry to the `_TIPS` dict in `deepagents_code/tui/widgets/startup_tip.py`, mapping the tip text to a relative selection weight (higher = shown more often). One tip is chosen at random above the input on startup to help users discover features. Keep tips short and action-oriented (e.g., `"Press ctrl+x to compose prompts in your external editor"`).
@@ -66,5 +66,6 @@ The main cost is the client/server boundary. When debugging, first decide which
66
66
 
67
67
  - For local setup and debugging, see [`DEVELOPMENT.md`](./DEVELOPMENT.md).
68
68
  - For command behavior, see [`COMMANDS.md`](./COMMANDS.md).
69
+ - For lifecycle hooks (`hooks.json`), see [`HOOKS.md`](./HOOKS.md).
69
70
  - For security boundaries, see [`THREAT_MODEL.md`](./THREAT_MODEL.md).
70
71
  - For package-specific coding conventions, see [`AGENTS.md`](./AGENTS.md).
@@ -2,6 +2,30 @@
2
2
 
3
3
  # Deep Agents Code Changelog
4
4
 
5
+ ## [0.1.50](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.49...deepagents-code==0.1.50) (2026-07-30)
6
+
7
+ ### Highlights
8
+
9
+ - Added project hooks workspace trust and expanded Hooks v2 support with client and server lifecycle events plus runtime feedback ([#5105](https://github.com/langchain-ai/deepagents/issues/5105), [#5104](https://github.com/langchain-ai/deepagents/issues/5104), [#4997](https://github.com/langchain-ai/deepagents/issues/4997), [#5045](https://github.com/langchain-ai/deepagents/issues/5045)).
10
+ - Added an option to mute the “YOLO is active” toast ([#5103](https://github.com/langchain-ai/deepagents/issues/5103)).
11
+ - Made the splash screen `thread` ID clickable to copy it ([#5173](https://github.com/langchain-ai/deepagents/issues/5173)).
12
+ - Show `ask_user` answers directly on the answered tool row ([#5100](https://github.com/langchain-ai/deepagents/issues/5100)).
13
+ - Show a toast when submitting an empty required `ask_user` answer ([#5095](https://github.com/langchain-ai/deepagents/issues/5095)).
14
+ - Added thread message counts to the Debug Console ([#5117](https://github.com/langchain-ai/deepagents/issues/5117)).
15
+
16
+ ### Fixes and improvements
17
+
18
+ - Gated Hooks v2 behind `DEEPAGENTS_CODE_EXPERIMENTAL` and improved hook resume stability across identity and Command tool results ([#5146](https://github.com/langchain-ai/deepagents/issues/5146), [#5176](https://github.com/langchain-ai/deepagents/issues/5176)).
19
+ - Kept server hook state out of task results ([#5164](https://github.com/langchain-ai/deepagents/issues/5164)).
20
+ - Stopped duplicate Auto transcript events during interrupt replay ([#5157](https://github.com/langchain-ai/deepagents/issues/5157)).
21
+ - Kept `/update` and `/install --package` prompts responsive ([#5127](https://github.com/langchain-ai/deepagents/issues/5127)).
22
+ - Refreshed the `/threads` cache after each turn ([#5174](https://github.com/langchain-ai/deepagents/issues/5174)).
23
+ - Anchored toasts above the chat input and added a toast when media is dropped into a free-text question ([#5101](https://github.com/langchain-ai/deepagents/issues/5101), [#5099](https://github.com/langchain-ai/deepagents/issues/5099)).
24
+ - Improved thread status message styling and links ([#5118](https://github.com/langchain-ai/deepagents/issues/5118)).
25
+ - Made resume hints echo the launched command name ([#5119](https://github.com/langchain-ai/deepagents/issues/5119)).
26
+ - Scoped selection copy to the clicked screen ([#5140](https://github.com/langchain-ai/deepagents/issues/5140)).
27
+ - Ignored mouse hits on detached widgets ([#5114](https://github.com/langchain-ai/deepagents/issues/5114)).
28
+
5
29
  ## [0.1.49](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.48...deepagents-code==0.1.49) (2026-07-27)
6
30
 
7
31
  ### Features
@@ -30,7 +30,7 @@ aliases, descriptions, visibility, or hidden-command metadata.
30
30
  | `/manual` | | Switch to Manual approval mode |
31
31
  | `/mcp` | | Manage MCP servers and authentication |
32
32
  | `/model` | | Switch models or edit model settings |
33
- | `/notifications` | | Configure startup warnings |
33
+ | `/notifications` | | Configure warning notifications |
34
34
  | `/offload` | `/compact` | Summarize and offload older messages to free context |
35
35
  | `/plugins` | | Manage plugins |
36
36
  | `/quit` | `/q` | Exit app |
@@ -0,0 +1,121 @@
1
+ # Hooks
2
+
3
+ Hooks are user-configured shell commands that run at agent lifecycle events. Each matching handler receives a JSON event payload on stdin and may influence the session through its exit code and stdout.
4
+
5
+ > **Warning:** Hook commands run on your machine with your user privileges. Treat every `hooks.json` entry as code you trust — especially project-scoped hooks checked into a repository.
6
+
7
+ ## Configuration locations and precedence
8
+
9
+ | Scope | Path | When it loads |
10
+ | --- | --- | --- |
11
+ | User | `~/.deepagents/hooks.json` | Always (when the file exists) |
12
+ | Project | `{project_root}/.deepagents/hooks.json` | Only after workspace trust |
13
+
14
+ When both scopes load, project matcher groups are applied first, then user groups. A project handler that stops further processing therefore wins over lower-precedence user handlers for the same event.
15
+
16
+ ### Project workspace trust
17
+
18
+ Project-scoped hooks can execute arbitrary commands from the repository. Before they load:
19
+
20
+ - Interactive `dcode` prompts for approval when `.deepagents/hooks.json` is present and the workspace is not already trusted.
21
+ - Choosing always-allow persists trust for that canonical workspace root in `~/.deepagents/.state/hooks_trust.json`.
22
+ - Cancelling the prompt (Esc / Ctrl+D) aborts startup.
23
+ - Denying skips project hooks for the session and continues with user hooks only.
24
+ - Headless / CI runs do not prompt; pass `--trust-project-hooks` to opt in for that run.
25
+
26
+ ## Events and matchers
27
+
28
+ Each top-level key under `"hooks"` is an event name. Values are lists of matcher groups. A group may omit `matcher` (or use `"*"`) to match all values for that event's matcher field. Events with no matcher field reject non-wildcard matchers at load time.
29
+
30
+ Native tools are matched by their wire names (for example `execute` → `Bash`, `write_file` → `Write`).
31
+
32
+ | Event | Owner | Matcher field | Fires when |
33
+ | --- | --- | --- | --- |
34
+ | `SessionStart` | client | `cause` | A session starts (`startup`, `resume`, `clear`, `compact`) |
35
+ | `UserPromptSubmit` | client | _(none)_ | The user submits a prompt |
36
+ | `SessionEnd` | client | `cause` | A session ends |
37
+ | `PermissionRequest` | client | `tool_name` | The client is about to ask for tool permission |
38
+ | `Notification` | client | `notification_type` | A client lifecycle notification is emitted |
39
+ | `PreToolUse` | server | `tool_name` | Before a tool call runs |
40
+ | `PostToolUse` | server | `tool_name` | After a tool call completes |
41
+ | `PreCompact` | server | `trigger` | Before conversation compaction |
42
+ | `Stop` | server | _(none)_ | After an agent stop turn |
43
+ | `SubagentStart` | server | `agent_name` | When a subagent starts |
44
+ | `SubagentStop` | server | `agent_name` | When a subagent stops |
45
+
46
+ ## Handler shape
47
+
48
+ Each matcher group has a `hooks` list of command handlers:
49
+
50
+ ```json
51
+ {
52
+ "type": "command",
53
+ "command": "your-shell-command",
54
+ "timeout": 60,
55
+ "statusMessage": "Running policy check"
56
+ }
57
+ ```
58
+
59
+ - `type` must be `"command"`.
60
+ - `command` is required and runs through a shell, so pipes, redirects, and `$VAR` expansion work.
61
+ - `argv` is optional; when set, the handler is executed directly from that argument list instead of through a shell.
62
+ - `timeout` is optional seconds; when omitted, the event default applies (600s for most events, 30s for `UserPromptSubmit`).
63
+ - `statusMessage` is optional UI status text while the handler runs.
64
+ - `async: true` is rejected; async command hooks are not supported.
65
+
66
+ ## Examples
67
+
68
+ ### Minimal
69
+
70
+ ```json
71
+ {
72
+ "hooks": {
73
+ "SessionStart": [
74
+ {
75
+ "hooks": [
76
+ {
77
+ "type": "command",
78
+ "command": "true"
79
+ }
80
+ ]
81
+ }
82
+ ]
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### Deny a destructive shell command
88
+
89
+ Matchers use wire tool names. `execute` is exposed as `Bash`. Exit code `2` (or JSON `permissionDecision: "deny"`) denies `PreToolUse`:
90
+
91
+ ```json
92
+ {
93
+ "hooks": {
94
+ "PreToolUse": [
95
+ {
96
+ "matcher": "Bash",
97
+ "hooks": [
98
+ {
99
+ "type": "command",
100
+ "command": "python3 -c \"import json,sys; d=json.load(sys.stdin); cmd=d.get('tool_input',{}).get('command',''); blocked='rm -rf /' in cmd; print(json.dumps({'hookSpecificOutput':{'hookEventName':'PreToolUse','permissionDecision':'deny','permissionDecisionReason':'Refusing destructive root delete'}}) if blocked else '{}')\""
101
+ }
102
+ ]
103
+ }
104
+ ]
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## How handler output affects behavior
110
+
111
+ Handlers communicate through:
112
+
113
+ - **Exit code `2`**: treated as a synthetic `decision: "block"`. Interpretation depends on the event (for example deny on `PreToolUse` / `PermissionRequest`, block further processing on `UserPromptSubmit` / `PreCompact`, feedback on `PostToolUse`).
114
+ - **Other non-zero exits**: recorded as diagnostics; they do not apply a block decision.
115
+ - **JSON stdout** (`HookWireOutput`): may set `continue` / `stopReason`, `systemMessage` (user-visible notice), `additionalContext` via `hookSpecificOutput`, and event-specific fields such as `permissionDecision` on `PreToolUse`.
116
+ - **Non-JSON stdout**: becomes additional context for events whose plain-output policy is context (`SessionStart`, `UserPromptSubmit`); otherwise it is a diagnostic.
117
+ - **Timeouts**: when a handler exceeds its timeout, it is terminated and recorded as a timeout diagnostic; it does not apply a successful decision.
118
+
119
+ ## Legacy configuration
120
+
121
+ Older list-shaped `hooks.json` documents are still loaded. Semantically equivalent legacy events are migrated into the Hooks v2 shape automatically; unsupported legacy events are left unmapped and surfaced as load diagnostics.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) LangChain, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -105,13 +105,13 @@ bootstrap: ## Install dev dependencies and git hooks for local development
105
105
  check: ## Run the full local CI gauntlet (lint, types, imports, tests, repo gates)
106
106
  check: lint check_imports test
107
107
  @echo "🔍 Verifying extras stay in sync with required deps..."
108
- uv run --all-groups python "$(REPO_ROOT)/.github/scripts/check_extras_sync.py" pyproject.toml
108
+ uv run --all-groups python "$(REPO_ROOT)/.github/scripts/checks/check_extras_sync.py" pyproject.toml
109
109
  @echo "🔍 Verifying pyproject.toml and _version.py agree..."
110
- uv run --all-groups python "$(REPO_ROOT)/.github/scripts/check_version_equality.py"
110
+ uv run --all-groups python "$(REPO_ROOT)/.github/scripts/checks/check_version_equality.py"
111
111
  @echo "🔒 Verifying uv.lock is up-to-date..."
112
112
  UV_FROZEN=false uv lock --check
113
113
  @echo "🔗 Checking SDK pin against workspace SDK (advisory)..."
114
- @uv run --all-groups python "$(REPO_ROOT)/.github/scripts/check_sdk_pin.py" || \
114
+ @uv run --all-groups python "$(REPO_ROOT)/.github/scripts/release/check_sdk_pin.py" || \
115
115
  { rc=$$?; [ $$rc -eq 1 ] && echo "⚠️ Stale SDK pin (advisory, non-fatal)." || exit $$rc; }
116
116
  @echo "✅ All local CI checks passed!"
117
117
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents-code
3
- Version: 0.1.49
3
+ Version: 0.1.50
4
4
  Summary: Terminal coding agent built on the Deep Agents SDK - works with any model, with persistent memory, customizable skills, subagents, and approval controls.
5
5
  Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
6
6
  Project-URL: Documentation, https://reference.langchain.com/python/deepagents/
@@ -11,6 +11,7 @@ Project-URL: Twitter, https://x.com/langchain_oss
11
11
  Project-URL: Slack, https://www.langchain.com/join-community
12
12
  Project-URL: Reddit, https://www.reddit.com/r/LangChain/
13
13
  License: MIT
14
+ License-File: LICENSE
14
15
  Keywords: agents,ai,cli,deep-agent,langchain,langgraph,llm,terminal
15
16
  Classifier: Development Status :: 4 - Beta
16
17
  Classifier: Environment :: Console
@@ -28,10 +29,10 @@ Requires-Python: <4.0,>=3.11
28
29
  Requires-Dist: aiosqlite<1.0.0,>=0.22.1
29
30
  Requires-Dist: anyio<5.0.0,>=4.14.2
30
31
  Requires-Dist: deepagents-acp<1.0.0,>=0.0.9
31
- Requires-Dist: deepagents==0.7.0b2
32
+ Requires-Dist: deepagents==0.7.0
32
33
  Requires-Dist: filelock<3.30,>=3.29.7
33
34
  Requires-Dist: httpx<1.0.0,>=0.28.1
34
- Requires-Dist: langchain-anthropic<2.0.0,>=1.5.2
35
+ Requires-Dist: langchain-anthropic<2.0.0,>=1.5.3
35
36
  Requires-Dist: langchain-google-genai<5.0.0,>=4.3.2
36
37
  Requires-Dist: langchain-mcp-adapters<1.0.0,>=0.3.0
37
38
  Requires-Dist: langchain-openai<2.0.0,>=1.4.1
@@ -63,7 +64,7 @@ Provides-Extra: agentcore
63
64
  Requires-Dist: langchain-agentcore-codeinterpreter<1.0.0,>=0.0.5; extra == 'agentcore'
64
65
  Provides-Extra: all-providers
65
66
  Requires-Dist: aiohttp<3.15.0,>=3.14.3; extra == 'all-providers'
66
- Requires-Dist: langchain-anthropic<2.0.0,>=1.5.2; extra == 'all-providers'
67
+ Requires-Dist: langchain-anthropic<2.0.0,>=1.5.3; extra == 'all-providers'
67
68
  Requires-Dist: langchain-aws<2.0.0,>=1.6.3; extra == 'all-providers'
68
69
  Requires-Dist: langchain-baseten<1.0.0,>=0.2.3; extra == 'all-providers'
69
70
  Requires-Dist: langchain-cohere<1.0.0,>=0.6.0; extra == 'all-providers'
@@ -91,7 +92,7 @@ Requires-Dist: langchain-modal>=0.0.5; extra == 'all-sandboxes'
91
92
  Requires-Dist: langchain-runloop>=0.0.6; extra == 'all-sandboxes'
92
93
  Requires-Dist: langchain-vercel-sandbox>=0.0.1; extra == 'all-sandboxes'
93
94
  Provides-Extra: anthropic
94
- Requires-Dist: langchain-anthropic<2.0.0,>=1.5.2; extra == 'anthropic'
95
+ Requires-Dist: langchain-anthropic<2.0.0,>=1.5.3; extra == 'anthropic'
95
96
  Provides-Extra: baseten
96
97
  Requires-Dist: langchain-baseten<1.0.0,>=0.2.3; extra == 'baseten'
97
98
  Provides-Extra: bedrock
@@ -17,7 +17,7 @@
17
17
  - Agent creation (`agent.py`)
18
18
  - Built-in tools (`tools.py`: `http_request`, `web_search`, `fetch_url`)
19
19
  - MCP config loader and per-server allow/deny lists (`mcp_tools.py`, `model_config.py`)
20
- - Hook dispatcher (`hooks.py`)
20
+ - Hook runtimes (`hooks/`)
21
21
  - Sandbox integration factory (`integrations/sandbox_factory.py`, `integrations/sandbox_provider.py`)
22
22
  - Session persistence (`sessions.py`)
23
23
  - Configuration system (`config.py`, `model_config.py`)
@@ -126,7 +126,7 @@
126
126
  | C3 | Agent Engine | LangGraph agent graph running inside `langgraph dev` server, assembled by `create_cli_agent` | framework-controlled | Yes | `agent.create_cli_agent`, `agent._add_interrupt_on`, `server_graph.make_graph` |
127
127
  | C4 | Built-in Tools | `http_request`, `web_search` (Tavily), `fetch_url` (HTML→markdown) | framework-controlled | Partial¹ | `tools.http_request`, `tools.web_search`, `tools.fetch_url` |
128
128
  | C5 | MCP Loader & Trust | Discovers/loads `.mcp.json`, validates server configs, applies per-server allow/deny lists + interactive approval | framework-controlled | No² | `mcp_tools.resolve_and_load_mcp_tools`, `model_config.load_mcp_server_trust_lists`, `main._check_mcp_project_trust` |
129
- | C6 | Hook Dispatcher | Fires subprocess commands on agent lifecycle events | framework-controlled | No³ | `hooks.dispatch_hook`, `hooks._run_single_hook` |
129
+ | C6 | Hook Runtime | Fires subprocess commands on agent lifecycle events | framework-controlled | No³ | `hooks.runtime.HooksRuntime`, `hooks.runner.run_command_handler` |
130
130
  | C7 | Sandbox Integration | Creates/destroys remote sandboxes (Daytona, LangSmith, Modal, Runloop, AgentCore) | framework-controlled | No⁴ | `integrations.sandbox_factory.create_sandbox` |
131
131
  | C8 | Session Persistence | SQLite checkpoint store for LangGraph thread state | framework-controlled | Yes | `sessions.get_db_path`, `sessions.generate_thread_id` |
132
132
  | C9 | Configuration System | TOML config, env vars, `AGENTS.md` system prompts, model config | user-controlled | N/A | `config.settings`, `model_config.ModelConfig`, `~/.deepagents/config.toml` |
@@ -142,7 +142,7 @@
142
142
  **Notes:**
143
143
  1. `http_request` and `fetch_url` enabled by default; `web_search` requires `TAVILY_API_KEY`.
144
144
  2. MCP servers only load if `.mcp.json` config files are present.
145
- 3. Hooks only execute if `~/.deepagents/hooks.json` exists.
145
+ 3. User hooks load from `~/.deepagents/hooks.json`. Project hooks load from `.deepagents/hooks.json` only after interactive workspace trust or the headless `--trust-project-hooks` opt-in.
146
146
  4. Sandbox mode requires explicit `--sandbox` CLI flag.
147
147
  5. Both TUI and non-interactive modes now always spawn a local LangGraph dev server and connect via `RemoteAgent`.
148
148
  6. `LocalContextMiddleware` is added whenever `LocalShellBackend` or an `_AsyncExecutableBackend` is in use (`agent.py:create_cli_agent`).
@@ -200,7 +200,7 @@
200
200
  | TB2 | LLM Decision → Tool Execution | HITL gate on all side-effecting tool calls | Interrupt map, allow-list check, auto-approve toggle | LLM reasoning; what user approves |
201
201
  | TB3 | Tool Result → LLM Context | Tool outputs re-enter the context window | Unicode warnings on URLs in args; markdownify HTML conversion | Content of fetched web pages, MCP responses, search results |
202
202
  | TB4 | MCP Config → Process / Network | `.mcp.json` triggers subprocess spawn or network connection | Schema validation; per-server allow/deny lists + interactive approval prompt | What the MCP process does once trusted and running |
203
- | TB5 | Hooks Config → Subprocess | `hooks.json` commands execute as local subprocesses | JSON structure validation, 5-second timeout | Command content (user-authored); env vars available to child |
203
+ | TB5 | Hooks Config → Subprocess | `hooks.json` commands execute as local subprocesses | Workspace trust for project hooks, schema validation, bounded execution, sanitized environment | Command content (user-authored) |
204
204
  | TB6 | Setup Script → Sandbox Execution | User-supplied script runs inside sandbox at startup | `shlex.quote()` wrapping; `string.Template.safe_substitute` | Script content (user-authored); sandbox network access |
205
205
  | TB7 | External LLM API → Agent State | LLM API responses drive tool call decisions | LLM client configuration, model selection, request params | LLM output content; provider-side safety |
206
206
  | TB8 | CLI → Server Subprocess IPC | Config passed from CLI to `langgraph dev` subprocess via `DA_SERVER_*` env vars | `ServerConfig.to_env()` serialization; env var scoping to parent+child process | Subprocess environment post-fork; /proc visibility to same-uid processes |
@@ -463,7 +463,7 @@ Threats that appear valid in isolation but fall outside project responsibility b
463
463
  | Malicious MCP server injecting prompt instructions | Users configure MCP servers and explicitly trust project-level configs. Once trusted, MCP tool outputs are data from a system the user controls. | Interactive approval prompt + per-server allow/deny lists for project-level configs (`main._check_mcp_project_trust`, `model_config.load_mcp_server_trust_lists`). |
464
464
  | LLM jailbreak / safety bypass | Model selection and safety configuration are user-controlled. The project routes prompts to the configured LLM but cannot guarantee model behavior. | Correctly routing prompts to the configured LLM; applying the system prompt from `agent.get_system_prompt`. |
465
465
  | Sandbox provider security vulnerabilities | Daytona, LangSmith, Modal, Runloop, and AgentCore are third-party services. Their internal security is not this project's responsibility. | Correctly initializing sandbox sessions via `integrations.sandbox_factory.create_sandbox`. |
466
- | Hook commands doing harmful things | Hooks in `~/.deepagents/hooks.json` are 100% user-authored. The payload is data-only (JSON on stdin). | JSON structure validation (`hooks._load_hooks`); 5-second timeout. |
466
+ | Hook commands doing harmful things | User-scoped hooks (`~/.deepagents/hooks.json`) and project-scoped hooks (`.deepagents/hooks.json`, only after interactive workspace trust or `--trust-project-hooks`) are intentionally configured commands. The payload is data-only (JSON on stdin). | Schema validation (`hooks.loading.load_hooks_config`); workspace trust for project hooks (versioned store under `~/.deepagents/.state/hooks_trust.json`; cancelling the trust prompt aborts startup); bounded execution with per-event default timeouts (600s for most events, 30s for `UserPromptSubmit`); sanitized subprocess environment. |
467
467
  | Async subagent traffic interception / MitM | Async subagents connect to user-configured LangGraph deployment URLs. The project does not control those endpoints or their TLS certificates. | Accepting URL/headers from user config and passing them to the LangGraph SDK (`agent.load_async_subagents`). |
468
468
  | LangGraph dev server port enumeration / discovery | Discovering the local dev server port requires local access. Port scanning localhost is a general OS security concern, not a framework vulnerability. | Binding to `127.0.0.1` by default (`server._DEFAULT_HOST`); ephemeral server lifetime; OS-assigned ephemeral port (`server._EPHEMERAL_PORT`) is not predictable across runs. |
469
469
  | `.env` file from parent directory changes app/API configuration | `config._find_dotenv_from_start_path` walks up the directory tree to find `.env` files. Discovering ordinary configuration values (API keys, `DEEPAGENTS_CODE_*` settings) this way is standard `python-dotenv` behavior, and the user controls their filesystem. The *code-execution* implication of a project `.env` (shell startup hooks) is tracked in-scope as T12. | Finding `.env` from the project root (`config._find_dotenv_from_start_path`); `override=False` by default (existing env vars preserved); shell startup / environment-hijack keys (`BASH_ENV`, `ENV`) denied during dotenv loading. |
@@ -503,3 +503,4 @@ Threats that appear valid in isolation but fall outside project responsibility b
503
503
  | 2026-07-08 | manual update | Removed the SHA-256 config fingerprint trust store (`mcp_trust.py`, `~/.deepagents/.state/mcp_trust.json`, DC4). Project MCP trust is now the interactive approval prompt (allow-for-session / always-allow scoped to project root + server-definition fingerprint / deny), the `--trust-project-mcp` run flag, the `[mcp].enabled_project_server_approvals` and `[mcp].disabled_project_servers` lists, and the process-wide `DEEPAGENTS_CODE_DANGEROUSLY_ENABLE_PROJECT_MCP_SERVERS` name-based escape hatch. The legacy flat `[mcp].enabled_project_servers` key is ignored. Persisted approvals bind to a server definition's fingerprint rather than a whole-config fingerprint. Updated C5, TB4, T10, the configuration input-coverage row, and the malicious-MCP-server dismissal accordingly |
504
504
  | 2026-07-21 | manual update | Clarified TB4 after process-wide MCP names and scoped remembered approvals changed from replacement semantics to independent grants. An empty process-wide allowlist no longer suppresses remembered approvals; disabled-server precedence is unchanged. |
505
505
  | 2026-07-22 | manual update | Added T13 (first-token shell allow-list bypass via allow-listed interpreters/wrappers) under TB2, distinguishing it from T2's `--shell-allow-list all` sentinel; cross-referenced it from T2 and extended the "LLM output" input-coverage gap to note that allow-list matching only inspects the command's first token |
506
+ | 2026-07-24 | manual update | Corrected the out-of-scope hooks row: Hooks v2 defaults are 600s (30s for `UserPromptSubmit`), not a global 5-second timeout; loading is `hooks.loading.load_hooks_config`; project hooks require workspace trust or `--trust-project-hooks` |
@@ -0,0 +1,257 @@
1
+ """Lightweight types and shared rendering for the ask-user interrupt protocol.
2
+
3
+ Extracted from `ask_user` so `textual_adapter` can import `AskUserRequest` at
4
+ module level — and `app` can reference the types at type-check time — without
5
+ pulling in the langchain middleware stack.
6
+
7
+ This is the shared wire format for the tool, the TUI, and `auto_mode`, colocated
8
+ so no consumer has to import another. Each symbol's own docstring records which
9
+ side produces it and which reads it.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Annotated, Literal, NotRequired
15
+
16
+ from pydantic import Field
17
+ from typing_extensions import TypedDict
18
+
19
+
20
+ class Choice(TypedDict):
21
+ """A single choice option for a multiple choice question."""
22
+
23
+ value: Annotated[str, Field(description="The display label for this choice.")]
24
+
25
+
26
+ class Question(TypedDict):
27
+ """A question to ask the user."""
28
+
29
+ question: Annotated[str, Field(description="The question text to display.")]
30
+
31
+ type: Annotated[
32
+ Literal["text", "multiple_choice"],
33
+ Field(
34
+ description=(
35
+ "Question type. 'text' for free-form input, 'multiple_choice' for "
36
+ "predefined options."
37
+ )
38
+ ),
39
+ ]
40
+
41
+ choices: NotRequired[
42
+ Annotated[
43
+ list[Choice],
44
+ Field(
45
+ description=(
46
+ "Options for multiple_choice questions. An 'Other' free-form "
47
+ "option is always appended automatically."
48
+ )
49
+ ),
50
+ ]
51
+ ]
52
+
53
+ required: NotRequired[
54
+ Annotated[
55
+ bool,
56
+ Field(
57
+ description="Whether the user must answer. Defaults to true if omitted."
58
+ ),
59
+ ]
60
+ ]
61
+
62
+
63
+ class AskUserRequest(TypedDict):
64
+ """Request payload sent via interrupt when asking the user questions."""
65
+
66
+ type: Literal["ask_user"]
67
+ """Discriminator tag, always `'ask_user'`."""
68
+
69
+ questions: list[Question]
70
+ """Questions to present to the user."""
71
+
72
+ tool_call_id: str
73
+ """ID of the originating tool call, used to route the response back."""
74
+
75
+
76
+ ASK_USER_AUTHORIZATION_METADATA_KEY = "deepagents_code_ask_user_authorization"
77
+ MAX_ASK_USER_AUTHORIZATION_ANSWER_CHARS = 4000
78
+
79
+
80
+ class AskUserAuthorizationReceipt(TypedDict):
81
+ """Trusted same-turn authorization recorded after an ask_user response."""
82
+
83
+ version: Literal[1]
84
+ thread_id: str
85
+ turn_id: str
86
+ tool_call_id: str
87
+ answers: list[str]
88
+
89
+
90
+ class AskUserAnswered(TypedDict):
91
+ """Widget result when the user submits answers."""
92
+
93
+ type: Literal["answered"]
94
+ """Discriminator tag, always `'answered'`."""
95
+
96
+ answers: list[str]
97
+ """User-provided answers, one per question."""
98
+
99
+
100
+ class AskUserCancelled(TypedDict):
101
+ """Widget result when the user cancels the prompt."""
102
+
103
+ type: Literal["cancelled"]
104
+ """Discriminator tag, always `'cancelled'`."""
105
+
106
+
107
+ AskUserWidgetResult = AskUserAnswered | AskUserCancelled
108
+ """Discriminated union for the ask_user widget Future result."""
109
+
110
+
111
+ ASK_USER_NO_ANSWER = "(no answer)"
112
+ """Placeholder for a question with no positionally matching answer.
113
+
114
+ Unreachable from `_parse_answers` today: every branch there emits exactly
115
+ `len(questions)` answers, and the answered branch rejects a mismatch outright
116
+ rather than formatting it. Kept as a guard for any future caller that formats a
117
+ transcript without that check. A question the user deliberately left blank
118
+ renders as an empty `A:` line instead, never as this placeholder.
119
+ """
120
+
121
+ ASK_USER_CANCELLED_ANSWER = "(cancelled)"
122
+ """Placeholder recorded for every question when the user cancels the prompt."""
123
+
124
+ ASK_USER_ERROR_ANSWER_PREFIX = "(error: "
125
+ """Prefix of the placeholder recorded for every question when the prompt fails.
126
+
127
+ The full placeholder is `(error: <detail>)`. Producer-side only: no production
128
+ code matches on it — `test_ask_user_types` is the only consumer, and it asserts
129
+ against this constant. The TUI derives its row summary from the recorded tool
130
+ status instead, because the placeholder is in-band — the prefix alone cannot
131
+ distinguish a failed prompt from a user who typed `(error: ...)` as their answer.
132
+ """
133
+
134
+ AskUserRowSummary = Literal["User answered", "Question failed"]
135
+ """The summaries an `ask_user` row may collapse to.
136
+
137
+ Narrows `ToolCallMessage.defer_success` so the transcript cannot be passed where
138
+ a summary belongs — the constraint that keeps user-typed answers out of
139
+ `tool.result` hook payloads.
140
+
141
+ Of the four `*_SUMMARY` constants below, only `ASK_USER_ANSWERED_SUMMARY` and
142
+ `ASK_USER_FAILED_SUMMARY` are members: the other two are hook bodies that no row
143
+ ever renders. `Literal[...]` cannot reference a plain string constant, so those
144
+ two values are restated here; both are annotated with this alias, so `ty` rejects
145
+ a reword that drifts. `test_row_summary_alias_matches_its_constants` pins it too.
146
+ """
147
+
148
+ ASK_USER_ANSWERED_SUMMARY: AskUserRowSummary = "User answered"
149
+ """One-line summary shown for an answered `ask_user` row before it is expanded.
150
+
151
+ Doubles as the `tool.result` hook payload's `tool_output` for an answered prompt
152
+ whose `ToolMessage` arrived, deliberately in place of the transcript, so
153
+ user-typed answers are not forwarded to hook scripts. When no `ToolMessage`
154
+ arrives the hook reports `ASK_USER_ANSWERED_NO_RESULT_SUMMARY` instead, while
155
+ the row still settles to this string. Rewording changes that hook contract as
156
+ well as the row; see `textual_adapter` and its `tool.result` tests.
157
+ """
158
+
159
+ ASK_USER_ANSWERED_NO_RESULT_SUMMARY = "User answered (no tool result)"
160
+ """The `tool.result` hook payload for an answered prompt that never completed.
161
+
162
+ Reported by `_dispatch_terminal_tool_result_hooks` when a teardown closes out a
163
+ row carrying a deferred success — the agent crashed, the stream ended, or the
164
+ user cancelled the turn. The guard there matches an already-settled row too, not
165
+ only one still awaiting its `ToolMessage`; see
166
+ `ToolCallMessage.deferred_success_output`.
167
+
168
+ The status stays `"success"` because the answers did reach the graph and only the
169
+ tool's own completion was lost, and `ask_user` results double as authorization
170
+ records; this distinct body is what lets an audit consumer tell "answers
171
+ delivered and the tool completed" from "answers delivered, then the turn died".
172
+ When the answers were never delivered at all the hook reports
173
+ `ASK_USER_ANSWERED_NOT_DELIVERED_SUMMARY` with `tool_status="error"` instead.
174
+ Hook contract: rewording it changes what those consumers see.
175
+ """
176
+
177
+ ASK_USER_ANSWERED_NOT_DELIVERED_SUMMARY = "User answered (answers not delivered)"
178
+ """The `tool.result` hook payload for answers the turn discarded before resuming.
179
+
180
+ Reported with `tool_status="error"` when a sibling question in the same batch was
181
+ cancelled: `textual_adapter` aborts the turn *before* `Command(resume=...)`, so
182
+ the resume payload — including this prompt's answers — is dropped, and the inline
183
+ widget is already unmounted, making the answers unrecoverable. The user answered,
184
+ but nothing downstream ever saw it.
185
+
186
+ Distinct from `ASK_USER_ANSWERED_NO_RESULT_SUMMARY`, and an error rather than a
187
+ success, precisely because `ask_user` results double as authorization records: a
188
+ `"success"` here would record an authorization that never took effect. Hook
189
+ contract: rewording it changes what audit consumers see.
190
+ """
191
+
192
+ ASK_USER_CANCELLED_SUMMARY = "Question cancelled"
193
+ """The `tool.result` hook payload's `tool_output` for the cancelled path.
194
+
195
+ Rewording it changes that hook contract. Not rendered on any row: a live cancel
196
+ calls `set_rejected` (which records no output), and a transcript of `(cancelled)`
197
+ placeholders from a non-TUI client is summarized from the recorded status like
198
+ any other, so it reads as `ASK_USER_ANSWERED_SUMMARY`. The cancel banner in
199
+ `textual_adapter` shares this wording but deliberately not this constant — it is
200
+ user-facing prose, not the hook contract.
201
+ """
202
+
203
+ ASK_USER_FAILED_SUMMARY: AskUserRowSummary = "Question failed"
204
+ """One-line summary shown for an `ask_user` prompt the middleware reported failed.
205
+
206
+ Also the `tool.result` hook payload's `tool_output` on that path — an answered
207
+ prompt whose `ToolMessage` came back with `status="error"` — deliberately in
208
+ place of the transcript, whose `(error: ...)` placeholders carry an arbitrary
209
+ detail string. Live widget failures are not this: they report their own error
210
+ text (see `textual_adapter`'s invalid-payload and cancel branches). Rewording
211
+ changes that hook contract as well as the row.
212
+ """
213
+
214
+
215
+ def format_ask_user_error_answer(detail: str) -> str:
216
+ """Render the placeholder answer recorded for every question on failure.
217
+
218
+ Args:
219
+ detail: Human-readable reason the prompt failed.
220
+
221
+ Returns:
222
+ The `(error: <detail>)` placeholder.
223
+ """
224
+ return f"{ASK_USER_ERROR_ANSWER_PREFIX}{detail})"
225
+
226
+
227
+ def format_ask_user_transcript(questions: list[Question], answers: list[str]) -> str:
228
+ r"""Render questions and answers as the `Q:`/`A:` transcript.
229
+
230
+ This is the text the `ask_user` tool returns to the model and persists in the
231
+ thread. The TUI renders that authoritative text literally rather than trying
232
+ to parse the unrestricted answer content back into structured data.
233
+
234
+ Answers are interpolated unescaped, so the encoding is not unambiguously
235
+ decodable: an answer containing a blank line followed by a literal
236
+ `Q: <text>\nA:` header is indistinguishable from a real block boundary. Only
237
+ the model reads it that way today. Any future decoder must anchor on the known
238
+ question text rather than on a generic `Q: ` pattern, or a crafted answer can
239
+ fabricate an extra question/answer pair.
240
+
241
+ Args:
242
+ questions: Questions that were asked. Callers must pass questions whose
243
+ `question` text is a non-empty string; `ask_user._validate_questions`
244
+ enforces that before interrupting. The empty default below only
245
+ keeps a caller that skips validation from raising `KeyError`.
246
+ answers: Answers, positionally matched to `questions`. A missing entry
247
+ falls back to `ASK_USER_NO_ANSWER`; extra entries are dropped.
248
+
249
+ Returns:
250
+ Blank-line separated `Q: ...\nA: ...` blocks, one per question.
251
+ """
252
+ blocks = [
253
+ f"Q: {question.get('question', '')}\n"
254
+ f"A: {answers[i] if i < len(answers) else ASK_USER_NO_ANSWER}"
255
+ for i, question in enumerate(questions)
256
+ ]
257
+ return "\n\n".join(blocks)
@@ -1,3 +1,3 @@
1
1
  """Generated at build time. Do not edit or commit."""
2
2
 
3
- BUILD_COMMIT = "44910bc"
3
+ BUILD_COMMIT = "63adb96"