drydock-cli 3.1.29__tar.gz → 3.1.31__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 (225) hide show
  1. {drydock_cli-3.1.29/drydock_cli.egg-info → drydock_cli-3.1.31}/PKG-INFO +1 -1
  2. drydock_cli-3.1.31/drydock/asr.py +227 -0
  3. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/cli.py +9 -0
  4. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/config.py +6 -0
  5. drydock_cli-3.1.31/drydock/mission.py +471 -0
  6. drydock_cli-3.1.31/drydock/mission_cli.py +362 -0
  7. drydock_cli-3.1.31/drydock/mission_run.py +593 -0
  8. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/ratchet.py +16 -11
  9. {drydock_cli-3.1.29 → drydock_cli-3.1.31/drydock_cli.egg-info}/PKG-INFO +1 -1
  10. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock_cli.egg-info/SOURCES.txt +9 -0
  11. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/pyproject.toml +1 -1
  12. drydock_cli-3.1.31/tests/test_asr.py +140 -0
  13. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_capacity.py +2 -2
  14. drydock_cli-3.1.31/tests/test_main_api_key.py +76 -0
  15. drydock_cli-3.1.31/tests/test_mission.py +136 -0
  16. drydock_cli-3.1.31/tests/test_mission_cli.py +155 -0
  17. drydock_cli-3.1.31/tests/test_mission_run.py +465 -0
  18. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_ratchet.py +13 -0
  19. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/LICENSE +0 -0
  20. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/NOTICE +0 -0
  21. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/README.md +0 -0
  22. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/__init__.py +0 -0
  23. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/__main__.py +0 -0
  24. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/advisor.py +0 -0
  25. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/agent.py +0 -0
  26. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/bash_safety.py +0 -0
  27. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/bottleneck.py +0 -0
  28. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/budget.py +0 -0
  29. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/__init__.py +0 -0
  30. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/document-canvas.md +0 -0
  31. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/fiar-assess.md +0 -0
  32. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/fiar-cap.md +0 -0
  33. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/fiar-evidence.md +0 -0
  34. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/fiar-readiness.md +0 -0
  35. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/ml-data.md +0 -0
  36. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/ml-debug.md +0 -0
  37. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/ml-finetune.md +0 -0
  38. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/ml-metrics.md +0 -0
  39. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/ml-rl.md +0 -0
  40. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/ml-train.md +0 -0
  41. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/nist-ai-rmf.md +0 -0
  42. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/nist-csf.md +0 -0
  43. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/rmf-categorize.md +0 -0
  44. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/rmf-control.md +0 -0
  45. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/rmf-poam.md +0 -0
  46. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/rmf-review.md +0 -0
  47. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/stig-assess.md +0 -0
  48. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/builtin_skills/stig-remediate.md +0 -0
  49. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/capacity.py +0 -0
  50. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/cci.py +0 -0
  51. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/comms/__init__.py +0 -0
  52. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/comms/attention.py +0 -0
  53. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/comms/channels.py +0 -0
  54. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/comms/events.py +0 -0
  55. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/compaction.py +0 -0
  56. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/detect.py +0 -0
  57. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/doccanvas.py +0 -0
  58. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/eratchet.py +0 -0
  59. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/events.py +0 -0
  60. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/extract.py +0 -0
  61. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/fiar.py +0 -0
  62. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/gittools.py +0 -0
  63. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/graphrag.py +0 -0
  64. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/groundtruth.py +0 -0
  65. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/guards.py +0 -0
  66. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/jobs.py +0 -0
  67. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/loop_detect.py +0 -0
  68. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/mcp.py +0 -0
  69. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/pdfredbox.py +0 -0
  70. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/phases.py +0 -0
  71. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/poam.py +0 -0
  72. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/predictions.py +0 -0
  73. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/progress.py +0 -0
  74. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/providers.py +0 -0
  75. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/recipes.py +0 -0
  76. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/recovery.py +0 -0
  77. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/resume.py +0 -0
  78. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/rmf.py +0 -0
  79. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/rmf_graph.py +0 -0
  80. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/skills.py +0 -0
  81. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/stig.py +0 -0
  82. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/subagents.py +0 -0
  83. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/suggest.py +0 -0
  84. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/swarm.py +0 -0
  85. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/task_state.py +0 -0
  86. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tool_policy.py +0 -0
  87. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tool_registry.py +0 -0
  88. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tool_result.py +0 -0
  89. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tool_select.py +0 -0
  90. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tool_validate.py +0 -0
  91. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tools/__init__.py +0 -0
  92. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/trajectory.py +0 -0
  93. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tui/__init__.py +0 -0
  94. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tui/app.py +0 -0
  95. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tui/approval.py +0 -0
  96. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tui/messages.py +0 -0
  97. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tui/widgets.py +0 -0
  98. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/tuning.py +0 -0
  99. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/verification.py +0 -0
  100. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock/web.py +0 -0
  101. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock_cli.egg-info/dependency_links.txt +0 -0
  102. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock_cli.egg-info/entry_points.txt +0 -0
  103. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock_cli.egg-info/requires.txt +0 -0
  104. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/drydock_cli.egg-info/top_level.txt +0 -0
  105. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/setup.cfg +0 -0
  106. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_advisor.py +0 -0
  107. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_approval.py +0 -0
  108. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_back_command.py +0 -0
  109. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_background.py +0 -0
  110. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_binary_output.py +0 -0
  111. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_crossplatform.py +0 -0
  112. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_output_bounding.py +0 -0
  113. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_process_group.py +0 -0
  114. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_safety.py +0 -0
  115. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_sanitize.py +0 -0
  116. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_shell.py +0 -0
  117. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_stdin.py +0 -0
  118. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_stop_partial.py +0 -0
  119. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_timeout_network.py +0 -0
  120. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bash_timeout_param.py +0 -0
  121. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_bottleneck.py +0 -0
  122. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_budget.py +0 -0
  123. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_cci.py +0 -0
  124. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_cli_agents.py +0 -0
  125. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_comms.py +0 -0
  126. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_compact_command.py +0 -0
  127. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_compaction.py +0 -0
  128. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_config.py +0 -0
  129. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_config_migration.py +0 -0
  130. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_context_limit_config.py +0 -0
  131. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_context_limit_issue25.py +0 -0
  132. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_criteria_coverage.py +0 -0
  133. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_cycling_detection.py +0 -0
  134. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_declared_deps.py +0 -0
  135. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_degenerate_argument.py +0 -0
  136. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_detect.py +0 -0
  137. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_dispatch.py +0 -0
  138. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_doccanvas.py +0 -0
  139. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_e2e_connected.py +0 -0
  140. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_edit_replace_all.py +0 -0
  141. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_edit_thrash_and_compaction.py +0 -0
  142. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_effort_governor.py +0 -0
  143. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_empty_response.py +0 -0
  144. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_eratchet.py +0 -0
  145. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_events.py +0 -0
  146. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_extract.py +0 -0
  147. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_failure_loop.py +0 -0
  148. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_fiar.py +0 -0
  149. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_first_run_setup.py +0 -0
  150. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_gittools.py +0 -0
  151. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_glob_edit_edges.py +0 -0
  152. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_graphify_example.py +0 -0
  153. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_graphrag.py +0 -0
  154. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_graphrag_quoted_path.py +0 -0
  155. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_graphrag_sqlite.py +0 -0
  156. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_grep_and_read_robust.py +0 -0
  157. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_groundtruth_predictions.py +0 -0
  158. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_guards_and_tools.py +0 -0
  159. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_hallucinated_tools.py +0 -0
  160. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_jobs.py +0 -0
  161. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_knowledge_tool_available.py +0 -0
  162. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_leaked_tool_call.py +0 -0
  163. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_loop_detect.py +0 -0
  164. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_mcp.py +0 -0
  165. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_model_registry.py +0 -0
  166. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_oneshot_unreachable.py +0 -0
  167. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_overthink_interrupt.py +0 -0
  168. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_pdfredbox.py +0 -0
  169. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_phases.py +0 -0
  170. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_plan_autocontinue.py +0 -0
  171. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_poam.py +0 -0
  172. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_progress.py +0 -0
  173. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_providers_unreachable.py +0 -0
  174. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_ratchet_offer.py +0 -0
  175. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_read_index.py +0 -0
  176. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_recipes.py +0 -0
  177. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_recovery.py +0 -0
  178. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_recovery_config.py +0 -0
  179. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_recovery_integration.py +0 -0
  180. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_repeated_outcome.py +0 -0
  181. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_repetition_interrupt.py +0 -0
  182. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_resume_events.py +0 -0
  183. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_resume_restore.py +0 -0
  184. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_resume_snapshot.py +0 -0
  185. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_rmf.py +0 -0
  186. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_rmf_graph.py +0 -0
  187. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_rmf_stig_graph.py +0 -0
  188. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_rolling_plan.py +0 -0
  189. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_runaway_repetition.py +0 -0
  190. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_screenshot.py +0 -0
  191. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_server_probe.py +0 -0
  192. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_skills.py +0 -0
  193. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_sqlite_events.py +0 -0
  194. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_stall_retry.py +0 -0
  195. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_stig.py +0 -0
  196. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_stop.py +0 -0
  197. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_streaming_newlines.py +0 -0
  198. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_subagent.py +0 -0
  199. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_subagents.py +0 -0
  200. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_suggest.py +0 -0
  201. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_swarm.py +0 -0
  202. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_system_prompt_help.py +0 -0
  203. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_task_state.py +0 -0
  204. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_timeline.py +0 -0
  205. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_todo.py +0 -0
  206. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tool_arg_coercion.py +0 -0
  207. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tool_arg_coercion_more.py +0 -0
  208. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tool_arg_parsing.py +0 -0
  209. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tool_canonical.py +0 -0
  210. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tool_policy.py +0 -0
  211. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tool_result.py +0 -0
  212. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tool_select.py +0 -0
  213. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tool_validate.py +0 -0
  214. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tools_undo.py +0 -0
  215. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_trajectory.py +0 -0
  216. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tui.py +0 -0
  217. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_tuning.py +0 -0
  218. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_verification_gate.py +0 -0
  219. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_viewimage.py +0 -0
  220. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_vision_input.py +0 -0
  221. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_web_tools.py +0 -0
  222. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_windows_shell.py +0 -0
  223. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_worker_subagent.py +0 -0
  224. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_write_content_coerce.py +0 -0
  225. {drydock_cli-3.1.29 → drydock_cli-3.1.31}/tests/test_xccdf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.1.29
3
+ Version: 3.1.31
4
4
  Summary: Drydock — a local, provider-agnostic terminal coding agent for local LLMs
5
5
  Author: Frank Bobe III
6
6
  License-Expression: Apache-2.0
@@ -0,0 +1,227 @@
1
+ """Adaptive Swarm Runtime (ASR) — the search-decision core.
2
+
3
+ This is the resource-agnostic brain of the adaptive swarm (PRD "Resource-Aware Adaptive
4
+ Swarms"): it turns a compute/time BUDGET into decisions about *where the next unit of inference
5
+ should go* and *when to stop*. It deliberately holds no model, GPU, or scheduler state — those
6
+ live in capacity.py (concurrency), swarm.py (blackboard/candidates/worktrees) and the mission
7
+ store (durable state). Keeping the decisions pure makes them unit-testable and reusable.
8
+
9
+ Three pieces, in dependency order:
10
+ * Budget — a finite termination envelope parsed from a user string (§15).
11
+ * Convergence — should the swarm keep going? Stops on success / budget / no-improvement /
12
+ when marginal value Δquality/Δcompute falls below a floor (§23).
13
+ * AdaptiveAllocator — split the next compute pool across live branches by expected value,
14
+ pruning the weak ones (§17). Bad ideas must not get the same compute as good.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import re
19
+ from dataclasses import dataclass, field
20
+
21
+ # ── Budget (§15) ────────────────────────────────────────────────────────────
22
+ _DUR = re.compile(r"^\s*([\d.]+)\s*([hms])\s*$", re.I)
23
+ _TOK = re.compile(r"^\s*([\d.]+)\s*([kmg]?)\s*[-_]?tokens?\s*$", re.I)
24
+ _MULT = {"": 1, "k": 1_000, "m": 1_000_000, "g": 1_000_000_000}
25
+
26
+
27
+ @dataclass
28
+ class Budget:
29
+ """A finite stopping envelope (§10/§15). Every field is optional, but a Budget with no
30
+ finite limit is a bug — `is_finite` guards against "run forever"."""
31
+ wall_secs: float | None = None
32
+ tokens: int | None = None
33
+ generations: int | None = None
34
+
35
+ @property
36
+ def is_finite(self) -> bool:
37
+ return any(v is not None for v in (self.wall_secs, self.tokens, self.generations))
38
+
39
+ @classmethod
40
+ def parse(cls, spec: str) -> "Budget":
41
+ """Parse one user token: '2h' / '30m' / '90s' (wall) or '100M-tokens' / '50k-tokens'
42
+ (compute). Unknown strings raise ValueError so a typo never silently means 'unbounded'."""
43
+ s = (spec or "").strip()
44
+ md = _DUR.match(s)
45
+ if md:
46
+ n, unit = float(md.group(1)), md.group(2).lower()
47
+ return cls(wall_secs=n * {"h": 3600, "m": 60, "s": 1}[unit])
48
+ mt = _TOK.match(s)
49
+ if mt:
50
+ return cls(tokens=int(float(mt.group(1)) * _MULT[mt.group(2).lower()]))
51
+ raise ValueError(f"unparseable budget: {spec!r} (try '2h', '30m', '100M-tokens')")
52
+
53
+
54
+ # ── Convergence (§23) ─────────────────────────────────────────────────────────
55
+ @dataclass
56
+ class GenerationRecord:
57
+ gen: int
58
+ best_score: float # best branch quality so far (higher = better)
59
+ cumulative_tokens: int # total inference tokens spent through this generation
60
+ elapsed_s: float # wall-clock since start
61
+
62
+
63
+ @dataclass
64
+ class Convergence:
65
+ """Decides whether the swarm should run another generation (§23). "More agents are not
66
+ automatically better": we stop the moment any finite condition trips, INCLUDING the marginal
67
+ one — when Δquality per unit compute drops below `min_marginal`, extra inference is waste."""
68
+ budget: Budget
69
+ success_score: float = 100.0 # stop when best branch reaches this (e.g. all tests pass)
70
+ no_improve_gens: int = 3 # stop after this many generations with no best-score gain
71
+ min_marginal: float = 0.0 # floor on Δscore per 1k tokens; <=0 disables the check
72
+ eps: float = 1e-9
73
+ history: list[GenerationRecord] = field(default_factory=list)
74
+
75
+ def record(self, best_score: float, cumulative_tokens: int, elapsed_s: float) -> None:
76
+ self.history.append(GenerationRecord(len(self.history) + 1, best_score,
77
+ cumulative_tokens, elapsed_s))
78
+
79
+ def _gens_since_improvement(self) -> int:
80
+ """Generations since the best score was FIRST reached (a plateau at the best counts)."""
81
+ if not self.history:
82
+ return 0
83
+ best = max(r.best_score for r in self.history)
84
+ first_best = next(i for i, r in enumerate(self.history) if r.best_score >= best - self.eps)
85
+ return (len(self.history) - 1) - first_best
86
+
87
+ def marginal_value(self) -> float | None:
88
+ """Δscore per 1,000 tokens over the last generation, or None with <2 records."""
89
+ if len(self.history) < 2:
90
+ return None
91
+ a, b = self.history[-2], self.history[-1]
92
+ dtok = b.cumulative_tokens - a.cumulative_tokens
93
+ if dtok <= 0:
94
+ return None
95
+ return (b.best_score - a.best_score) / (dtok / 1000.0)
96
+
97
+ def should_continue(self) -> tuple[bool, str]:
98
+ """(keep_going?, reason). Checked in priority order; the reason names the stop trigger."""
99
+ last = self.history[-1] if self.history else None
100
+ if last is not None and last.best_score >= self.success_score - self.eps:
101
+ return False, "success score reached"
102
+ b = self.budget
103
+ if b.wall_secs is not None and last is not None and last.elapsed_s >= b.wall_secs:
104
+ return False, "wall-clock budget exhausted"
105
+ if b.tokens is not None and last is not None and last.cumulative_tokens >= b.tokens:
106
+ return False, "token budget exhausted"
107
+ if b.generations is not None and len(self.history) >= b.generations:
108
+ return False, "generation budget reached"
109
+ if self.no_improve_gens > 0 and self._gens_since_improvement() >= self.no_improve_gens:
110
+ return False, f"no improvement in {self.no_improve_gens} generations"
111
+ mv = self.marginal_value()
112
+ if self.min_marginal > 0 and mv is not None and mv < self.min_marginal:
113
+ return False, f"marginal value {mv:.3f} < floor {self.min_marginal:.3f} (Δquality/Δcompute)"
114
+ return True, "continue"
115
+
116
+
117
+ # ── Adaptive compute allocation (§17) ──────────────────────────────────────────
118
+ @dataclass
119
+ class Allocation:
120
+ grants: dict[str, int] # branch id -> tokens granted this round
121
+ terminated: list[str] # branch ids pruned (0 tokens, killed)
122
+
123
+
124
+ def allocate(branches: list[tuple[str, float]], pool_tokens: int, *,
125
+ prune_below: float = 0.0, keep_top: int | None = None,
126
+ bias: float = 1.5, min_grant: int = 0) -> Allocation:
127
+ """Split `pool_tokens` across branches by expected value (§17).
128
+
129
+ branches: (id, score) with higher score = more promising. A branch is TERMINATED (gets 0)
130
+ if its score is below `prune_below` or it falls outside the top `keep_top`. Survivors share
131
+ the pool in proportion to score**bias, so leaders get disproportionately more (bias>1) — the
132
+ system must not spend equally on good and bad ideas. `min_grant` guarantees a floor so a
133
+ kept-but-trailing branch still gets a probe. Deterministic; ties broken by input order."""
134
+ if pool_tokens <= 0 or not branches:
135
+ return Allocation({}, [b for b, _ in branches])
136
+ ranked = sorted(branches, key=lambda x: x[1], reverse=True)
137
+ survivors = [(b, s) for b, s in ranked if s >= prune_below]
138
+ if keep_top is not None:
139
+ survivors = survivors[:max(0, keep_top)]
140
+ kept_ids = {b for b, _ in survivors}
141
+ terminated = [b for b, _ in branches if b not in kept_ids]
142
+ if not survivors:
143
+ return Allocation({}, terminated)
144
+ weights = [(b, max(s, 0.0) ** bias) for b, s in survivors]
145
+ total_w = sum(w for _, w in weights) or float(len(weights))
146
+ grants: dict[str, int] = {}
147
+ for b, w in weights:
148
+ share = (w / total_w) if total_w else 1.0 / len(weights)
149
+ grants[b] = max(min_grant, int(round(pool_tokens * share)))
150
+ # correct rounding drift so grants sum to the pool (adjust the top branch)
151
+ drift = pool_tokens - sum(grants.values())
152
+ if drift and weights:
153
+ top = weights[0][0]
154
+ grants[top] = max(min_grant, grants[top] + drift)
155
+ return Allocation(grants, terminated)
156
+
157
+
158
+ # ── Planner: budget + intensity + hardware → population plan (§15/§16, §5/§9) ──
159
+ # Diverse roles for solution-space coverage (§9/§10). Correlated agents waste compute.
160
+ ROLES = [
161
+ "first-principles solver", "conventional solver", "skeptic", "debugger",
162
+ "security reviewer", "researcher", "minimalist", "adversarial critic",
163
+ "performance specialist", "alternative-architecture agent",
164
+ ]
165
+
166
+ # Per-intensity search shape (§16): initial population, critic count, generation cap, and how
167
+ # many branches survive each prune. Population is a SEARCH choice — independent of hardware.
168
+ INTENSITY = {
169
+ "light": {"population": 3, "critics": 1, "generations": 2, "keep": 2},
170
+ "standard": {"population": 8, "critics": 2, "generations": 4, "keep": 4},
171
+ "deep": {"population": 16, "critics": 3, "generations": 6, "keep": 6},
172
+ "extreme": {"population": 24, "critics": 5, "generations": 10, "keep": 8},
173
+ }
174
+
175
+
176
+ @dataclass
177
+ class SwarmPlan:
178
+ intensity: str
179
+ initial_population: int # logical agents in the explore stage (§9)
180
+ max_concurrency: int # simultaneous inference — HARDWARE bound (§5), != population
181
+ critics: int # judge/critic agents (§11)
182
+ generations: int # generation cap (a convergence bound, §23)
183
+ keep: int # survivors promoted past each prune (§12)
184
+ roles: list[str] # diversified assignments (§10)
185
+ estimated_logical: int # rough total agents incl. spawned descendants (§13) — info only
186
+
187
+ @property
188
+ def queued_at_start(self) -> int:
189
+ """Logical agents that must wait because concurrency < population (§18)."""
190
+ return max(0, self.initial_population - self.max_concurrency)
191
+
192
+
193
+ def choose_intensity(budget: Budget) -> str:
194
+ """Map a budget to a search intensity for `--swarm auto` (§16). Bigger budget → deeper
195
+ search. Falls back to 'standard' when the budget carries no size signal."""
196
+ if budget.wall_secs is not None:
197
+ s = budget.wall_secs
198
+ return "light" if s < 900 else "standard" if s < 3600 else "deep" if s < 14400 else "extreme"
199
+ if budget.tokens is not None:
200
+ t = budget.tokens
201
+ return ("light" if t < 1_000_000 else "standard" if t < 10_000_000
202
+ else "deep" if t < 50_000_000 else "extreme")
203
+ if budget.generations is not None:
204
+ g = budget.generations
205
+ return "light" if g <= 2 else "standard" if g <= 4 else "deep" if g <= 6 else "extreme"
206
+ return "standard"
207
+
208
+
209
+ def plan(budget: Budget, concurrency: int, *, intensity: str = "auto") -> SwarmPlan:
210
+ """Turn a budget + measured hardware concurrency into a population plan (§15/§16).
211
+
212
+ KEY: logical population comes from the intensity (a search decision); `concurrency` only
213
+ caps how many run at once (§5). On a 1-GPU box a 16-agent 'deep' plan still has 16 logical
214
+ agents — the rest queue (§18); the penalty is wall-clock, not lost search capability. This
215
+ is the opposite of capacity.swarm_size(), which caps logical count AT concurrency."""
216
+ intensity = choose_intensity(budget) if intensity == "auto" else intensity
217
+ if intensity not in INTENSITY:
218
+ raise ValueError(f"unknown intensity {intensity!r} (light|standard|deep|extreme|auto)")
219
+ p = INTENSITY[intensity]
220
+ pop = p["population"]
221
+ roles = [ROLES[i % len(ROLES)] for i in range(pop)]
222
+ # estimate total logical agents: initial + ~one descendant per survivor per later generation
223
+ estimated = pop + p["keep"] * max(0, p["generations"] - 1)
224
+ return SwarmPlan(intensity=intensity, initial_population=pop,
225
+ max_concurrency=max(1, concurrency), critics=p["critics"],
226
+ generations=p["generations"], keep=p["keep"], roles=roles,
227
+ estimated_logical=estimated)
@@ -383,6 +383,15 @@ def main():
383
383
  cfg["cwd"] = os.getcwd()
384
384
  sys.exit(swarm_run_cli(sys.argv[2:], config=cfg))
385
385
 
386
+ # `drydock mission <create|status|run|resume|…>` — long-horizon autonomous missions.
387
+ if len(sys.argv) > 1 and sys.argv[1] == "mission":
388
+ from drydock import config as cfgmod
389
+ from drydock.mission_cli import run_cli as mission_run_cli
390
+ cfg = cfgmod.resolve({}, cfgmod.default_config_path())
391
+ cfgmod.resolve_active_model(cfg)
392
+ cfg["cwd"] = os.getcwd()
393
+ sys.exit(mission_run_cli(sys.argv[2:], config=cfg))
394
+
386
395
  parser = argparse.ArgumentParser(description="DryDock — local coding agent")
387
396
  parser.add_argument(
388
397
  "--version", "-V", action="version", version=f"drydock {__version__}"
@@ -27,6 +27,12 @@ DEFAULTS: dict[str, object] = {
27
27
  # default invisibly, which left base_url absent from the file. For a non-vllm
28
28
  # provider, override base_url (or use --base-url / the first-run prompt).
29
29
  "base_url": "http://localhost:8000/v1",
30
+ # API key for the MAIN model endpoint, sent as `Authorization: Bearer <key>`.
31
+ # Leave "" for a local server that needs no auth (llama.cpp/Ollama/LM Studio);
32
+ # set it for a hosted OpenAI-compatible endpoint (e.g. a vLLM behind a gateway,
33
+ # OpenRouter, Together). Mirrors `advisor_api_key`. Can also come from the
34
+ # provider's api_key_env (e.g. OPENAI_API_KEY); the config value wins.
35
+ "api_key": "",
30
36
  "max_tokens": 8192, # 4096 truncated large file writes mid-JSON (→ _raw fail)
31
37
  "temperature": 0.2,
32
38
  # The model server's context window (llama.cpp -c / vLLM --max-model-len).