drydock-cli 3.1.28__tar.gz → 3.1.29__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {drydock_cli-3.1.28/drydock_cli.egg-info → drydock_cli-3.1.29}/PKG-INFO +1 -1
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/capacity.py +61 -31
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/config.py +5 -4
- {drydock_cli-3.1.28 → drydock_cli-3.1.29/drydock_cli.egg-info}/PKG-INFO +1 -1
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/pyproject.toml +1 -1
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_capacity.py +29 -9
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/LICENSE +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/NOTICE +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/README.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/__init__.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/__main__.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/advisor.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/agent.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/bash_safety.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/bottleneck.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/budget.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/__init__.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/document-canvas.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/fiar-assess.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/fiar-cap.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/fiar-evidence.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/fiar-readiness.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/ml-data.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/ml-debug.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/ml-finetune.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/ml-metrics.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/ml-rl.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/ml-train.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/nist-ai-rmf.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/nist-csf.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/rmf-categorize.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/rmf-control.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/rmf-poam.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/rmf-review.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/stig-assess.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/builtin_skills/stig-remediate.md +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/cci.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/cli.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/comms/__init__.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/comms/attention.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/comms/channels.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/comms/events.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/compaction.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/detect.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/doccanvas.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/eratchet.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/events.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/extract.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/fiar.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/gittools.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/graphrag.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/groundtruth.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/guards.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/jobs.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/loop_detect.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/mcp.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/pdfredbox.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/phases.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/poam.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/predictions.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/progress.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/providers.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/ratchet.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/recipes.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/recovery.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/resume.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/rmf.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/rmf_graph.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/skills.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/stig.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/subagents.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/suggest.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/swarm.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/task_state.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tool_policy.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tool_registry.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tool_result.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tool_select.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tool_validate.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tools/__init__.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/trajectory.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tui/__init__.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tui/app.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tui/approval.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tui/messages.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tui/widgets.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/tuning.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/verification.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock/web.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock_cli.egg-info/SOURCES.txt +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock_cli.egg-info/dependency_links.txt +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock_cli.egg-info/entry_points.txt +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock_cli.egg-info/requires.txt +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/drydock_cli.egg-info/top_level.txt +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/setup.cfg +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_advisor.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_approval.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_back_command.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_background.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_binary_output.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_crossplatform.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_output_bounding.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_process_group.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_safety.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_sanitize.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_shell.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_stdin.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_stop_partial.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_timeout_network.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bash_timeout_param.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_bottleneck.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_budget.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_cci.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_cli_agents.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_comms.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_compact_command.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_compaction.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_config.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_config_migration.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_context_limit_config.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_context_limit_issue25.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_criteria_coverage.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_cycling_detection.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_declared_deps.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_degenerate_argument.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_detect.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_dispatch.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_doccanvas.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_e2e_connected.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_edit_replace_all.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_edit_thrash_and_compaction.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_effort_governor.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_empty_response.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_eratchet.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_events.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_extract.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_failure_loop.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_fiar.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_first_run_setup.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_gittools.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_glob_edit_edges.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_graphify_example.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_graphrag.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_graphrag_quoted_path.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_graphrag_sqlite.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_grep_and_read_robust.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_groundtruth_predictions.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_guards_and_tools.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_hallucinated_tools.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_jobs.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_knowledge_tool_available.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_leaked_tool_call.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_loop_detect.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_mcp.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_model_registry.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_oneshot_unreachable.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_overthink_interrupt.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_pdfredbox.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_phases.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_plan_autocontinue.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_poam.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_progress.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_providers_unreachable.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_ratchet.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_ratchet_offer.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_read_index.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_recipes.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_recovery.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_recovery_config.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_recovery_integration.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_repeated_outcome.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_repetition_interrupt.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_resume_events.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_resume_restore.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_resume_snapshot.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_rmf.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_rmf_graph.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_rmf_stig_graph.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_rolling_plan.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_runaway_repetition.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_screenshot.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_server_probe.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_skills.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_sqlite_events.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_stall_retry.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_stig.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_stop.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_streaming_newlines.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_subagent.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_subagents.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_suggest.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_swarm.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_system_prompt_help.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_task_state.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_timeline.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_todo.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tool_arg_coercion.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tool_arg_coercion_more.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tool_arg_parsing.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tool_canonical.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tool_policy.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tool_result.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tool_select.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tool_validate.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tools_undo.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_trajectory.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tui.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_tuning.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_verification_gate.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_viewimage.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_vision_input.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_web_tools.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_windows_shell.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_worker_subagent.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_write_content_coerce.py +0 -0
- {drydock_cli-3.1.28 → drydock_cli-3.1.29}/tests/test_xccdf.py +0 -0
|
@@ -6,14 +6,15 @@ agents just queue — more tokens, no more wall-clock. So "hammer the 8-GPU vLLM
|
|
|
6
6
|
down the -np 2 laptop" reduces to: estimate the server's concurrency C, then size the swarm
|
|
7
7
|
to min(C, task-demand, budget). This module estimates C.
|
|
8
8
|
|
|
9
|
-
Detection
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
2.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
Detection is FULLY AUTOMATIC — the user configures nothing. Order (first hit wins), all
|
|
10
|
+
swallow-error / never-raise:
|
|
11
|
+
1. llama.cpp — GET /slots (its `-np` parallel slots), a free exact read;
|
|
12
|
+
2. empirical ramp probe — double a burst of tiny concurrent requests until the server
|
|
13
|
+
saturates (server-agnostic; the honest signal for vLLM, whose max_num_seqs isn't exposed
|
|
14
|
+
over the OpenAI API — an 8-GPU box reads big, a -np 2 laptop reads small, with no config);
|
|
15
|
+
3. fallback — a conservative default.
|
|
16
|
+
An OPTIONAL escape hatch, checked first and needed by no one: `config['swarm_concurrency']`
|
|
17
|
+
or $DRYDOCK_SWARM_CONCURRENCY, for an operator who wants to pin it or skip probing entirely.
|
|
17
18
|
|
|
18
19
|
Stdlib-only; the probe funcs are injectable so this is testable without a live server.
|
|
19
20
|
"""
|
|
@@ -71,37 +72,66 @@ def _default_requester(base_url: str, model: str) -> Callable[[], None]:
|
|
|
71
72
|
return req
|
|
72
73
|
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
_PROBE_CACHE: dict[str, int] = {}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def clear_probe_cache() -> None:
|
|
79
|
+
"""Forget cached probe results (mainly for tests / a changed server)."""
|
|
80
|
+
_PROBE_CACHE.clear()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _safe_call(req: Callable[[], None]) -> None:
|
|
80
84
|
try:
|
|
81
|
-
t0 = time.monotonic()
|
|
82
85
|
req()
|
|
83
|
-
t1 = time.monotonic() - t0
|
|
84
86
|
except Exception: # noqa: BLE001
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _burst_wall(req: Callable[[], None], b: int) -> float:
|
|
88
91
|
start = time.monotonic()
|
|
89
92
|
try:
|
|
90
|
-
with concurrent.futures.ThreadPoolExecutor(max_workers=
|
|
91
|
-
list(ex.map(lambda _: _safe_call(req), range(
|
|
93
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=b) as ex:
|
|
94
|
+
list(ex.map(lambda _: _safe_call(req), range(b)))
|
|
92
95
|
except Exception: # noqa: BLE001
|
|
93
|
-
return 1
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
return -1.0
|
|
97
|
+
return time.monotonic() - start
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def probe_concurrency(base_url: str, model: str, *, max_probe: int = 64,
|
|
101
|
+
requester: Callable[[], None] | None = None, cache: bool = True) -> int:
|
|
102
|
+
"""Empirically estimate useful parallelism with ZERO configuration, by RAMPING the burst
|
|
103
|
+
until the server saturates — so an 8-GPU vLLM box is detected as big and a -np 2 laptop
|
|
104
|
+
as small, without anyone declaring anything.
|
|
105
|
+
|
|
106
|
+
One warm request costs t1; a burst of B concurrent requests finishes in wall W, so
|
|
107
|
+
parallelism P ≈ B·t1/W (≈B if fully batched, ≈1 if serialized). Ramp B = 4, 8, 16, …:
|
|
108
|
+
while the server keeps the whole burst parallel (P ≈ B) it can take more, so double B;
|
|
109
|
+
the first time it can't (P < B) is its ceiling. Cached per server (the probe costs a
|
|
110
|
+
handful of max_tokens=1 requests, run once)."""
|
|
111
|
+
if cache and base_url in _PROBE_CACHE:
|
|
112
|
+
return _PROBE_CACHE[base_url]
|
|
113
|
+
req = requester or _default_requester(base_url, model)
|
|
101
114
|
try:
|
|
115
|
+
t0 = time.monotonic()
|
|
102
116
|
req()
|
|
103
|
-
|
|
104
|
-
|
|
117
|
+
t1 = time.monotonic() - t0
|
|
118
|
+
except Exception: # noqa: BLE001 — server unreachable
|
|
119
|
+
return 1
|
|
120
|
+
if t1 <= 0:
|
|
121
|
+
result = max_probe
|
|
122
|
+
else:
|
|
123
|
+
best, burst = 1, 4
|
|
124
|
+
while burst <= max_probe:
|
|
125
|
+
wall = _burst_wall(req, burst)
|
|
126
|
+
p = burst if wall <= 0 else max(1, min(burst, round(burst * t1 / wall)))
|
|
127
|
+
best = max(best, p)
|
|
128
|
+
if p < burst * 0.75: # couldn't keep the whole burst parallel → ceiling
|
|
129
|
+
break
|
|
130
|
+
burst *= 2
|
|
131
|
+
result = best
|
|
132
|
+
if cache:
|
|
133
|
+
_PROBE_CACHE[base_url] = result
|
|
134
|
+
return result
|
|
105
135
|
|
|
106
136
|
|
|
107
137
|
def detect_concurrency(base_url: str, *, provider: str = "vllm", model: str = "",
|
|
@@ -102,10 +102,11 @@ DEFAULTS: dict[str, object] = {
|
|
|
102
102
|
# (docs/multi_agent_swarm_prd.md). On by default; set false to keep every request single-
|
|
103
103
|
# agent unless the user runs /swarm explicitly.
|
|
104
104
|
"swarm_auto_escalate": True,
|
|
105
|
-
# Swarm sizing: the harness
|
|
106
|
-
# (drydock/capacity.py
|
|
107
|
-
#
|
|
108
|
-
#
|
|
105
|
+
# Swarm sizing is FULLY AUTOMATIC: the harness detects the inference server's real
|
|
106
|
+
# concurrency (drydock/capacity.py — llama.cpp /slots, else an empirical ramp probe) and
|
|
107
|
+
# caps agents at it, so an 8-GPU vLLM box gets hammered and a -np 2 laptop toned down with
|
|
108
|
+
# NO configuration. swarm_concurrency is an optional pin (0 = auto-detect, the default);
|
|
109
|
+
# nobody needs to set it.
|
|
109
110
|
"swarm_concurrency": 0,
|
|
110
111
|
"swarm_concurrency_default": 4,
|
|
111
112
|
# URL substrings the web tools refuse: WebSearch drops matching results,
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
# PyPI distribution name is drydock-cli (the established install name, continued
|
|
8
8
|
# from the retired v2 fork); the import package + CLI command stay `drydock`.
|
|
9
9
|
name = "drydock-cli"
|
|
10
|
-
version = "3.1.
|
|
10
|
+
version = "3.1.29"
|
|
11
11
|
description = "Drydock — a local, provider-agnostic terminal coding agent for local LLMs"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.11"
|
|
@@ -59,20 +59,40 @@ def test_detect_order_slots_then_probe_then_default(monkeypatch):
|
|
|
59
59
|
assert capacity.detect_concurrency("http://x/v1", model="", config={"swarm_concurrency_default": 9}) == 9
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def
|
|
63
|
-
#
|
|
62
|
+
def test_probe_ramps_to_detect_a_big_parallel_server():
|
|
63
|
+
# a fully-batched server keeps every burst parallel → the ramp climbs past the old cap,
|
|
64
|
+
# so an 8-GPU vLLM box reads BIG with zero config
|
|
64
65
|
def parallel_req():
|
|
65
|
-
time.sleep(0.
|
|
66
|
+
time.sleep(0.02)
|
|
66
67
|
|
|
67
|
-
p = capacity.probe_concurrency("http://
|
|
68
|
-
|
|
68
|
+
p = capacity.probe_concurrency("http://big/v1", "m", max_probe=32,
|
|
69
|
+
requester=parallel_req, cache=False)
|
|
70
|
+
assert p >= 16
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
|
|
73
|
+
def test_probe_detects_a_small_serial_server():
|
|
74
|
+
# a lock forces one-at-a-time → the ramp stops immediately at ~1 (auto tone-down)
|
|
71
75
|
lock = threading.Lock()
|
|
72
76
|
|
|
73
77
|
def serial_req():
|
|
74
78
|
with lock:
|
|
75
|
-
time.sleep(0.
|
|
79
|
+
time.sleep(0.02)
|
|
80
|
+
|
|
81
|
+
s = capacity.probe_concurrency("http://small/v1", "m", max_probe=32,
|
|
82
|
+
requester=serial_req, cache=False)
|
|
83
|
+
assert s <= 3
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_probe_result_is_cached_per_server():
|
|
87
|
+
calls = {"n": 0}
|
|
88
|
+
|
|
89
|
+
def req():
|
|
90
|
+
calls["n"] += 1
|
|
91
|
+
time.sleep(0.005)
|
|
76
92
|
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
capacity.clear_probe_cache()
|
|
94
|
+
a = capacity.probe_concurrency("http://cache/v1", "m", max_probe=8, requester=req)
|
|
95
|
+
n_after_first = calls["n"]
|
|
96
|
+
assert n_after_first > 1 # it actually probed (warm + bursts)
|
|
97
|
+
b = capacity.probe_concurrency("http://cache/v1", "m", max_probe=8, requester=req)
|
|
98
|
+
assert a == b and calls["n"] == n_after_first # cached: no new requests
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|