dreadnode 2.0.8__tar.gz → 2.0.10__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.
- {dreadnode-2.0.8 → dreadnode-2.0.10}/PKG-INFO +2 -2
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/agent.py +13 -17
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/hooks.py +81 -103
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/mcp/auth.py +17 -8
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/mcp/client.py +32 -4
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/subagent.py +8 -4
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/tools.py +13 -7
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/trajectory.py +12 -4
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/assessment.py +164 -48
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/autodan_turbo.py +1 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/beast.py +4 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/crescendo.py +5 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/deep_inception.py +4 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/drattack.py +4 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/goat.py +5 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/gptfuzzer.py +4 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/image.py +4 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/multimodal.py +2 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/pair.py +5 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/prompt.py +104 -9
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/rainbow.py +5 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/renellm.py +4 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/tap.py +1 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/api/client.py +496 -53
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/api/models.py +40 -14
- dreadnode-2.0.10/dreadnode/app/cli/airt.py +1057 -0
- dreadnode-2.0.10/dreadnode/app/cli/capability.py +1218 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/dataset.py +72 -103
- dreadnode-2.0.10/dreadnode/app/cli/evaluation.py +1633 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/main.py +61 -4
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/model.py +82 -123
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/optimize.py +31 -26
- dreadnode-2.0.10/dreadnode/app/cli/sandbox.py +127 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/shared.py +169 -45
- dreadnode-2.0.10/dreadnode/app/cli/task.py +2277 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/__init__.py +1 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/__init__.py +1 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/challenge/Dockerfile +4 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/docker-compose.yaml +10 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/provision.sh +27 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/solution.sh +21 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/task-remote.yaml.tmpl +120 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/task.yaml.tmpl +99 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/teardown.sh +22 -0
- dreadnode-2.0.10/dreadnode/app/cli/templates/init/verify.sh +28 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/train.py +34 -42
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/worlds.py +2 -1
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/config.py +2 -1
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/main.py +79 -58
- dreadnode-2.0.10/dreadnode/app/model_catalog.py +205 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/server/app.py +718 -180
- dreadnode-2.0.10/dreadnode/app/server/prompt.py +217 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/app.py +852 -156
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/client.py +172 -76
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/commands.py +18 -2
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/dreadnode.tcss +146 -33
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/event_contract.py +13 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/model_variants.py +2 -163
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/__init__.py +6 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/base.py +2 -3
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/capabilities.py +277 -167
- dreadnode-2.0.10/dreadnode/app/tui/screens/capability_docs.py +301 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/environments.py +185 -24
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/mcp.py +21 -9
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/model_picker.py +1 -1
- dreadnode-2.0.10/dreadnode/app/tui/screens/models.py +875 -0
- dreadnode-2.0.10/dreadnode/app/tui/screens/raw_spans.py +186 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/runtimes.py +221 -135
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/sessions.py +17 -7
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/theme_showcase.py +1 -1
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/workspaces.py +104 -13
- dreadnode-2.0.10/dreadnode/app/tui/spans_reader.py +134 -0
- dreadnode-2.0.10/dreadnode/app/tui/turn_lifecycle.py +193 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/turn_reducer.py +12 -1
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/composer.py +36 -6
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/context_bar.py +2 -1
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/conversation.py +46 -4
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/status_bar.py +16 -11
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/tool_progress.py +1 -1
- dreadnode-2.0.10/dreadnode/builtin_capabilities/__init__.py +79 -0
- dreadnode-2.0.10/dreadnode/builtin_capabilities/dreadnode/agents/dreadnode.md +54 -0
- dreadnode-2.0.10/dreadnode/builtin_capabilities/dreadnode/capability.yaml +4 -0
- dreadnode-2.0.10/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-runtime-reference/SKILL.md +78 -0
- dreadnode-2.0.10/dreadnode/builtin_capabilities/dreadnode/system-prompt.md +7 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/capabilities/capability.py +29 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/capabilities/loader.py +228 -14
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/capabilities/sync.py +26 -30
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/capabilities/types.py +80 -8
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/util.py +11 -11
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/datasets/local.py +5 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/generator/base.py +39 -2
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/generator/litellm_.py +83 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/models/local.py +5 -0
- dreadnode-2.0.10/dreadnode/optimization/adapters/stack.py +601 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/backends/gepa.py +3 -1
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/jobs.py +47 -10
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/stopping.py +0 -1
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/study.py +149 -15
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/packaging/manifest.py +3 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/packaging/oci.py +3 -3
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/packaging/package.py +3 -0
- dreadnode-2.0.10/dreadnode/packaging/task_validation.py +993 -0
- dreadnode-2.0.10/dreadnode/skills/__init__.py +27 -0
- dreadnode-2.0.10/dreadnode/skills/creating-capabilities/SKILL.md +160 -0
- dreadnode-2.0.10/dreadnode/skills/creating-capabilities/capability-components.md +111 -0
- dreadnode-2.0.10/dreadnode/skills/creating-capabilities/capability-improvement.md +339 -0
- dreadnode-2.0.10/dreadnode/skills/creating-capabilities/runtime-default-capability.md +262 -0
- dreadnode-2.0.10/dreadnode/skills/dreadnode-cli/SKILL.md +109 -0
- dreadnode-2.0.10/dreadnode/skills/dreadnode-cli/references/command-groups.md +224 -0
- dreadnode-2.0.10/dreadnode/skills/dreadnode-cli/references/tui-crosswalk.md +27 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/__init__.py +35 -9
- dreadnode-2.0.10/dreadnode/tools/dreadnode_cli.py +63 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/ls.py +9 -3
- dreadnode-2.0.10/dreadnode/tools/report.py +95 -0
- dreadnode-2.0.10/dreadnode/tools/trajectory_search.py +63 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tracing/constants.py +3 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tracing/spans.py +12 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/jobs.py +13 -4
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/recipes.py +7 -5
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/refine.py +31 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/text.py +4 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/pyproject.toml +2 -2
- dreadnode-2.0.8/dreadnode/app/cli/airt.py +0 -392
- dreadnode-2.0.8/dreadnode/app/cli/capability.py +0 -594
- dreadnode-2.0.8/dreadnode/app/cli/evaluation.py +0 -202
- dreadnode-2.0.8/dreadnode/app/cli/task.py +0 -325
- dreadnode-2.0.8/dreadnode/app/server/prompt.py +0 -182
- dreadnode-2.0.8/dreadnode/packaging/task_validation.py +0 -441
- {dreadnode-2.0.8 → dreadnode-2.0.10}/.gitignore +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/LICENSE +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/README.md +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/__main__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/events.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/exceptions.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/format.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/mcp/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/mcp/config.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/mcp/server.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/reactions.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/skills.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/agents/stopping.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/analytics/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/analytics/aggregator.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/analytics/classifier.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/analytics/compliance.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/analytics/engine.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/analytics/recommendations.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/analytics/types.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/compliance/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/compliance/atlas.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/compliance/nist.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/compliance/owasp.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/compliance/owasp_agentic.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/compliance/saif.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/constants.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/assets/audio/adversarial_query.mp3 +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/assets/image/bomb.jpg +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/assets/image/meth.png +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/prompts/adversarial_benchmark_subset.csv +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/prompts/ai_safety.csv +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/data_exfiltration.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/goal_hijacking.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/memory_poisoning.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/privilege_escalation.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/rce.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/scope_creep.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/tool_chaining.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/tool_selection_safety.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/unbounded_agency.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/rubrics/web_chatbot_security.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/templates/crescendo/variant_1.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/templates/crescendo/variant_2.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/templates/crescendo/variant_3.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/templates/crescendo/variant_4.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/data/templates/crescendo/variant_5.yaml +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/events.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/reporting/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/reporting/json_report.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/reporting/llm_summary.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/airt/reporting/markdown.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/api/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/args.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/cli/runtime.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/print_mode.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/server/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/server/auth.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/server/utils.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/connection.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/runtime_cache.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/auth.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/connection_error.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/console.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/evaluations.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/sandboxes.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/secrets.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/screens/traces.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/theme.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/update_check.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/agent_dialog.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/agent_suggester.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/flash.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/header_bar.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/help_panel.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/mention_overlay.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/message_queue.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/overlay_mixin.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/permission_prompt.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/profile_dialog.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/prompt_info.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/session_sidebar.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/skills_dialog.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/slash_overlay.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/throbber.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/tool.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/tools_dialog.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/welcome.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/app/tui/widgets/whoami.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/capabilities/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/capabilities/tool_rules.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/conditions.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/discovery.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/environment.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/exceptions.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/execution.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/hook.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/judge.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/load.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/log.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/meta/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/meta/config.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/meta/context.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/meta/hydrate.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/meta/introspect.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/metric.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/object.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/scorer.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/serialization.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/stopping.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/task.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/transforms.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/audio.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/base.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/common.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/image.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/object_3d.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/table.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/text.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/core/types/video.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/datasets/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/datasets/dataset.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/datasets/hf.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/evaluations/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/evaluations/console.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/evaluations/evaluation.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/evaluations/events.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/evaluations/format.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/evaluations/result.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/evaluations/sample.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/caching.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/chat.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/data.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/exceptions.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/generator/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/generator/http.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/generator/transformers_.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/generator/vllm_.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/message.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/models.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/parsing.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/tokenizer/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/tokenizer/base.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/tokenizer/transformers_.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/generators/utils.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/models/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/models/hf.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/models/model.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/adapters/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/adapters/agent.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/api.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/backends/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/backends/base.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/collectors.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/config.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/console.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/events.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/format.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/result.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/sampler.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/sampling.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/search.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/optimization/trial.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/packaging/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/packaging/loader.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/py.typed +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/boundary.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/fuzzing.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/graph.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/grid.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/image.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/mapelites.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/optuna.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/random.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/registry.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/samplers/strategy.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/advanced_jailbreak_detection.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/agent_security.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/agentic.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/agentic_workflow.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/classification.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/consistency.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/contains.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/cosine_sim.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/credentials.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/crucible.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/documentation_security.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/exfiltration_detection.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/format.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/harm.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/ide_security.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/image.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/json.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/judge.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/length.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/lexical.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/mcp_security.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/memorization.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/multi_agent_security.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/pii.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/prompt_leak.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/readability.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/reasoning_security.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/sentiment.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/scorers/similarity.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/storage/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/storage/providers.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/storage/session_store.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/storage/storage.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/_ripgrep.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/apply_patch.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/editing.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/execute.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/fetch.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/glob.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/grep.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/interaction.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/memory.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/read.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/task.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/think.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/todo.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/web_search.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tools/write.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tracing/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tracing/convert.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tracing/exporter.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tracing/exporters.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tracing/span.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/tracing/trace_converter.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/base.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/dpo.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/etl/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/etl/_common.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/etl/rl.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/etl/sft.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/etl/worlds.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/events.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/grpo.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ppo.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/prime.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/async_trainer.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/config.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/coordinator.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/distributed.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/dpo.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/experience.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/fsdp2_learner.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/inference.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/learner.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/multi_turn.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/ppo.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/reward_model.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/rollout_env.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/rollout_worker.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/sft.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/ray/trainer.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rewards/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rewards/aggregator.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rewards/functions.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rewards/scorer_bridge.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rewards/shaping.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rewards/types.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rollouts/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rollouts/adapters.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rollouts/orchestrator.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rollouts/types.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/rollouts/worlds.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/serving/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/serving/vllm_client.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/sft.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/tinker/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/tinker/config.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/tinker/data.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/tinker/renderer.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/tinker/rl.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/tinker/trainer.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/tinker_sft.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/training/utils.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/__init__.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/advanced_jailbreak.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/adversarial_suffix.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/agent_skill.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/agentic_workflow.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/audio.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/browser_agent_attacks.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/cipher.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/constitutional.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/document.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/documentation_poison.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/encoding.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/exfiltration.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/flip_attack.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/guardrail_bypass.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/ide_injection.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/image.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/injection.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/json_tools.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/language.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/logic_bomb.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/mcp_attacks.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/multi_agent_attacks.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/persuasion.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/perturbation.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/pii_extraction.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/pythonic_tools.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/rag_poisoning.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/reasoning_attacks.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/response_steering.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/stylistic.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/substitution.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/swap.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/system_prompt_extraction.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/video.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/transforms/xml_tools.py +0 -0
- {dreadnode-2.0.8 → dreadnode-2.0.10}/dreadnode/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dreadnode
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.10
|
|
4
4
|
Summary: Dreadnode SDK
|
|
5
5
|
Project-URL: Homepage, https://dreadnode.io
|
|
6
6
|
Project-URL: Documentation, https://docs.dreadnode.io
|
|
@@ -19,7 +19,7 @@ Requires-Dist: gepa>=0.1.1
|
|
|
19
19
|
Requires-Dist: httpx<1.0.0,>=0.28.0
|
|
20
20
|
Requires-Dist: jsonpath-ng>=1.7.0
|
|
21
21
|
Requires-Dist: jsonref>=1.1.0
|
|
22
|
-
Requires-Dist: litellm<=1.
|
|
22
|
+
Requires-Dist: litellm<=1.83.0,>=1.83.0
|
|
23
23
|
Requires-Dist: logfire<=3.20.0,>=3.5.3
|
|
24
24
|
Requires-Dist: loguru>=0.7.3
|
|
25
25
|
Requires-Dist: mcp<2.0.0,>=1.25.0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import inspect
|
|
3
3
|
import typing as t
|
|
4
|
-
from contextlib import AsyncExitStack, asynccontextmanager,
|
|
4
|
+
from contextlib import AsyncExitStack, asynccontextmanager, suppress
|
|
5
5
|
from copy import deepcopy
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from textwrap import dedent
|
|
@@ -120,7 +120,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
120
120
|
)
|
|
121
121
|
judge: Judge[Rubric] | None = Config(default=None, repr=False)
|
|
122
122
|
trajectory: Trajectory = Field(default_factory=Trajectory, exclude=True, repr=False)
|
|
123
|
-
max_steps: int = Config(default=
|
|
123
|
+
max_steps: int = Config(default=1000, ge=1)
|
|
124
124
|
"""Maximum number of generation/tool steps before the agent stops."""
|
|
125
125
|
generation_timeout: int | None = Config(default=None)
|
|
126
126
|
"""Timeout in seconds for each LLM generation call. None = no timeout."""
|
|
@@ -423,7 +423,8 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
423
423
|
)
|
|
424
424
|
|
|
425
425
|
try:
|
|
426
|
-
summary = await summarize_conversation
|
|
426
|
+
summary = await summarize_conversation(
|
|
427
|
+
summarizer,
|
|
427
428
|
"\n".join(str(msg) for msg in to_summarize),
|
|
428
429
|
)
|
|
429
430
|
except Exception:
|
|
@@ -585,7 +586,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
585
586
|
agent_name=self.name,
|
|
586
587
|
inputs={"goal": self._current_input},
|
|
587
588
|
params={
|
|
588
|
-
"session_id": str(
|
|
589
|
+
"session_id": str(traj.session_id),
|
|
589
590
|
"max_steps": self.max_steps,
|
|
590
591
|
**({"model": self.model_name} if self.model_name else {}),
|
|
591
592
|
**({"tools": [tool.name for tool in self.all_tools]} if self.all_tools else {}),
|
|
@@ -682,7 +683,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
682
683
|
)
|
|
683
684
|
|
|
684
685
|
# Check stop conditions INSIDE span
|
|
685
|
-
if any(cond(
|
|
686
|
+
if any(cond(traj.steps) for cond in self.stop_conditions):
|
|
686
687
|
logger.info("A stop condition was met. Ending run.")
|
|
687
688
|
gen_end = GenerationEnd(
|
|
688
689
|
agent_id=self.agent_id,
|
|
@@ -871,7 +872,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
871
872
|
)
|
|
872
873
|
|
|
873
874
|
# Check stop conditions again INSIDE span
|
|
874
|
-
if any(cond(
|
|
875
|
+
if any(cond(traj.steps) for cond in self.stop_conditions):
|
|
875
876
|
break
|
|
876
877
|
|
|
877
878
|
except Continue as e:
|
|
@@ -898,7 +899,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
898
899
|
stop_reason = "error"
|
|
899
900
|
elif max_steps_reached:
|
|
900
901
|
stop_reason = "max_steps_reached"
|
|
901
|
-
elif
|
|
902
|
+
elif traj.steps and isinstance(traj.steps[-1], AgentStalled):
|
|
902
903
|
stop_reason = "stalled"
|
|
903
904
|
|
|
904
905
|
async for event in self._dispatch(
|
|
@@ -960,17 +961,12 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
960
961
|
if hasattr(tool_container, "__aenter__") and hasattr(tool_container, "__aexit__"):
|
|
961
962
|
await stack.enter_async_context(tool_container)
|
|
962
963
|
|
|
963
|
-
# Parent span only on new conversation (not when borrowing external trajectory)
|
|
964
964
|
display_name = self.name or str(self.agent_id)[:8]
|
|
965
|
-
ctx = (
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
tags=["agent"],
|
|
971
|
-
)
|
|
972
|
-
if reset and trajectory is None
|
|
973
|
-
else nullcontext()
|
|
965
|
+
ctx = dreadnode.task_span(
|
|
966
|
+
f"agent:{display_name}",
|
|
967
|
+
type="agent",
|
|
968
|
+
label=display_name,
|
|
969
|
+
tags=["agent"],
|
|
974
970
|
)
|
|
975
971
|
|
|
976
972
|
with ctx as parent_span:
|
|
@@ -21,7 +21,6 @@ from dreadnode.agents.events import (
|
|
|
21
21
|
)
|
|
22
22
|
from dreadnode.agents.reactions import Reaction, Retry
|
|
23
23
|
from dreadnode.core.hook import Hook, hook
|
|
24
|
-
from dreadnode.generators.chat import Chat
|
|
25
24
|
from dreadnode.generators.generator import Generator
|
|
26
25
|
from dreadnode.generators.message import Message, make_compaction_message
|
|
27
26
|
|
|
@@ -159,98 +158,98 @@ CONTEXT_LENGTH_ERROR_PATTERNS = [
|
|
|
159
158
|
class Summary:
|
|
160
159
|
analysis: str
|
|
161
160
|
summary: str
|
|
162
|
-
chat: Chat
|
|
163
161
|
|
|
164
162
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing development work without losing context.
|
|
169
|
-
|
|
170
|
-
Before providing your final summary, wrap your analysis in <analysis> tags to organize your thoughts and ensure you've covered all necessary points. In your analysis process:
|
|
171
|
-
|
|
172
|
-
1. Chronologically analyze each message and section of the conversation. For each section thoroughly identify:
|
|
173
|
-
- The user's explicit requests and intents
|
|
174
|
-
- Your approach to addressing the user's requests
|
|
175
|
-
- Key decisions, technical concepts and code patterns
|
|
176
|
-
- Specific technical details like paths, usernames, structured objects, and code
|
|
177
|
-
- Tool interactions performed with a specific focus on intent and outcome
|
|
178
|
-
- Errors that you ran into and how you fixed them
|
|
179
|
-
- Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.
|
|
180
|
-
|
|
181
|
-
2. Double-check for technical accuracy and completeness, addressing each required element thoroughly.
|
|
182
|
-
|
|
183
|
-
Your summary should include the following sections:
|
|
184
|
-
|
|
185
|
-
1. Primary Request and Intent: Capture all of the user's explicit requests and intents in detail
|
|
186
|
-
2. Key Technical Concepts: List all important technical concepts, technologies, and frameworks discussed.
|
|
187
|
-
3. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created. Pay special attention to the most recent messages and include full code snippets where applicable and include a summary of why this file read or edit is important.
|
|
188
|
-
4. Errors and fixes: List all errors that you ran into, and how you fixed them. Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.
|
|
189
|
-
5. Problem Solving: Document problems solved and any ongoing troubleshooting efforts.
|
|
190
|
-
6. All user messages: List ALL user messages that are not tool results. These are critical for understanding the users' feedback and changing intent.
|
|
191
|
-
7. Pending Tasks: Outline any pending tasks that you have explicitly been asked to work on.
|
|
192
|
-
8. Current Work: Describe in detail precisely what was being worked on immediately before this summary request, paying special attention to the most recent messages from both user and assistant. Include file names and code snippets where applicable.
|
|
193
|
-
9. Optional Next Step: List the next step that you will take that is related to the most recent work you were doing. IMPORTANT: ensure that this step is DIRECTLY in line with the user's explicit requests, and the task you were working on immediately before this summary request. If your last task was concluded, then only list next steps if they are explicitly in line with the users request. Do not start on tangential requests without confirming with the user first.
|
|
194
|
-
If there is a next step, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no drift in task interpretation.
|
|
195
|
-
|
|
196
|
-
Here's an example of how your output should be structured:
|
|
163
|
+
_SUMMARIZATION_SYSTEM_PROMPT = """\
|
|
164
|
+
Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions.
|
|
165
|
+
This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing development work without losing context.
|
|
197
166
|
|
|
198
|
-
|
|
199
|
-
<analysis>
|
|
200
|
-
[Your thought process, ensuring all points are covered thoroughly and accurately]
|
|
201
|
-
</analysis>
|
|
167
|
+
Before providing your final summary, wrap your analysis in <analysis> tags to organize your thoughts and ensure you've covered all necessary points. In your analysis process:
|
|
202
168
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
169
|
+
1. Chronologically analyze each message and section of the conversation. For each section thoroughly identify:
|
|
170
|
+
- The user's explicit requests and intents
|
|
171
|
+
- Your approach to addressing the user's requests
|
|
172
|
+
- Key decisions, technical concepts and code patterns
|
|
173
|
+
- Specific technical details like paths, usernames, structured objects, and code
|
|
174
|
+
- Tool interactions performed with a specific focus on intent and outcome
|
|
175
|
+
- Errors that you ran into and how you fixed them
|
|
176
|
+
- Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.
|
|
206
177
|
|
|
207
|
-
|
|
178
|
+
2. Double-check for technical accuracy and completeness, addressing each required element thoroughly.
|
|
208
179
|
|
|
209
|
-
|
|
210
|
-
- [Concept 2]
|
|
211
|
-
- [Object 1]
|
|
212
|
-
- [...]
|
|
180
|
+
Your summary should include the following sections:
|
|
213
181
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
- [...]
|
|
226
|
-
|
|
227
|
-
5. Problem Solving:
|
|
228
|
-
[Description of solved problems and ongoing troubleshooting]
|
|
182
|
+
1. Primary Request and Intent: Capture all of the user's explicit requests and intents in detail
|
|
183
|
+
2. Key Technical Concepts: List all important technical concepts, technologies, and frameworks discussed.
|
|
184
|
+
3. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created. Pay special attention to the most recent messages and include full code snippets where applicable and include a summary of why this file read or edit is important.
|
|
185
|
+
4. Errors and fixes: List all errors that you ran into, and how you fixed them. Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.
|
|
186
|
+
5. Problem Solving: Document problems solved and any ongoing troubleshooting efforts.
|
|
187
|
+
6. All user messages: List ALL user messages that are not tool results. These are critical for understanding the users' feedback and changing intent.
|
|
188
|
+
7. Pending Tasks: Outline any pending tasks that you have explicitly been asked to work on.
|
|
189
|
+
8. Current Work: Describe in detail precisely what was being worked on immediately before this summary request, paying special attention to the most recent messages from both user and assistant. Include file names and code snippets where applicable.
|
|
190
|
+
9. Optional Next Step: List the next step that you will take that is related to the most recent work you were doing. IMPORTANT: ensure that this step is DIRECTLY in line with the user's explicit requests, and the task you were working on immediately before this summary request. If your last task was concluded, then only list next steps if they are explicitly in line with the users request. Do not start on tangential requests without confirming with the user first.
|
|
191
|
+
If there is a next step, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no drift in task interpretation.
|
|
192
|
+
"""
|
|
229
193
|
|
|
230
|
-
|
|
194
|
+
_SUMMARIZATION_USER_TEMPLATE = """\
|
|
195
|
+
Please provide your summary based on the conversation below, following the system instructions and ensuring precision and thoroughness in your response.
|
|
231
196
|
|
|
232
|
-
|
|
233
|
-
|
|
197
|
+
{guidance_section}<conversation>
|
|
198
|
+
{conversation}
|
|
199
|
+
</conversation>"""
|
|
234
200
|
|
|
235
|
-
7. Pending Tasks:
|
|
236
201
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
202
|
+
async def summarize_conversation(
|
|
203
|
+
generator: "str | Generator",
|
|
204
|
+
conversation: str,
|
|
205
|
+
*,
|
|
206
|
+
guidance: str = "",
|
|
207
|
+
) -> Summary:
|
|
208
|
+
"""Run the summarization prompt against the given generator and return a Summary."""
|
|
209
|
+
import re
|
|
210
|
+
|
|
211
|
+
from dreadnode.generators.generator import GenerateParams
|
|
212
|
+
from dreadnode.generators.generator import get_generator as _get_generator
|
|
213
|
+
|
|
214
|
+
if isinstance(generator, str):
|
|
215
|
+
generator = _get_generator(generator)
|
|
216
|
+
|
|
217
|
+
guidance_section = ""
|
|
218
|
+
if guidance:
|
|
219
|
+
guidance_section = f"Additional summarization guidance:\n{guidance}\n\n"
|
|
220
|
+
|
|
221
|
+
messages = [
|
|
222
|
+
Message(role="system", content=_SUMMARIZATION_SYSTEM_PROMPT),
|
|
223
|
+
Message(
|
|
224
|
+
role="user",
|
|
225
|
+
content=_SUMMARIZATION_USER_TEMPLATE.format(
|
|
226
|
+
guidance_section=guidance_section,
|
|
227
|
+
conversation=conversation,
|
|
228
|
+
),
|
|
229
|
+
),
|
|
230
|
+
]
|
|
240
231
|
|
|
241
|
-
|
|
242
|
-
|
|
232
|
+
results = await generator.generate_messages(
|
|
233
|
+
[messages],
|
|
234
|
+
[generator.params or GenerateParams()],
|
|
235
|
+
)
|
|
243
236
|
|
|
244
|
-
|
|
245
|
-
|
|
237
|
+
result = results[0]
|
|
238
|
+
if isinstance(result, BaseException):
|
|
239
|
+
raise result
|
|
246
240
|
|
|
247
|
-
|
|
248
|
-
</example>
|
|
241
|
+
response_text = result.message.content
|
|
249
242
|
|
|
250
|
-
|
|
243
|
+
analysis = ""
|
|
244
|
+
summary_text = response_text
|
|
245
|
+
analysis_match = re.search(r"<analysis>(.*?)</analysis>", response_text, re.DOTALL)
|
|
246
|
+
if analysis_match:
|
|
247
|
+
analysis = analysis_match.group(1).strip()
|
|
248
|
+
summary_match = re.search(r"<summary>(.*?)</summary>", response_text, re.DOTALL)
|
|
249
|
+
if summary_match:
|
|
250
|
+
summary_text = summary_match.group(1).strip()
|
|
251
251
|
|
|
252
|
-
|
|
253
|
-
"""
|
|
252
|
+
return Summary(analysis=analysis, summary=summary_text)
|
|
254
253
|
|
|
255
254
|
|
|
256
255
|
def _is_context_length_error(error: BaseException) -> bool:
|
|
@@ -273,8 +272,8 @@ def find_summarization_boundary(
|
|
|
273
272
|
|
|
274
273
|
Walks messages from the start and finds the latest safe split point that
|
|
275
274
|
leaves at least ``min_messages_to_keep`` messages in the "keep" portion.
|
|
276
|
-
|
|
277
|
-
|
|
275
|
+
A safe boundary is after a simple assistant message (no tool calls) —
|
|
276
|
+
this is the natural end of a complete conversational turn.
|
|
278
277
|
|
|
279
278
|
Returns:
|
|
280
279
|
Index splitting ``messages[:boundary]`` (to summarize) from
|
|
@@ -288,10 +287,7 @@ def find_summarization_boundary(
|
|
|
288
287
|
is_simple_assistant = message.role == "assistant" and not getattr(
|
|
289
288
|
message, "tool_calls", None
|
|
290
289
|
)
|
|
291
|
-
|
|
292
|
-
i + 1 == len(messages) or messages[i + 1].role != "tool"
|
|
293
|
-
)
|
|
294
|
-
if is_simple_assistant or is_last_tool:
|
|
290
|
+
if is_simple_assistant:
|
|
295
291
|
best_boundary = i + 1
|
|
296
292
|
return best_boundary
|
|
297
293
|
|
|
@@ -371,7 +367,8 @@ def _make_summarize_hook(
|
|
|
371
367
|
if not to_summarize:
|
|
372
368
|
return None
|
|
373
369
|
|
|
374
|
-
summary = await summarize_conversation
|
|
370
|
+
summary = await summarize_conversation(
|
|
371
|
+
summarizer_model,
|
|
375
372
|
"\n".join(str(msg) for msg in to_summarize),
|
|
376
373
|
guidance=guidance,
|
|
377
374
|
)
|
|
@@ -398,22 +395,3 @@ def _make_summarize_hook(
|
|
|
398
395
|
|
|
399
396
|
# Pre-instantiated with defaults -- this is what the wrapper discovers
|
|
400
397
|
summarize_when_long = _make_summarize_hook()
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
# =============================================================================
|
|
404
|
-
# Default Hooks
|
|
405
|
-
# =============================================================================
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
def default_hooks() -> list:
|
|
409
|
-
"""Core hooks every agent should have."""
|
|
410
|
-
from dreadnode.agents import stopping
|
|
411
|
-
|
|
412
|
-
hooks: list = [
|
|
413
|
-
tool_metrics(),
|
|
414
|
-
summarize_when_long,
|
|
415
|
-
stopping.step_count(50, name="server_step_limit"),
|
|
416
|
-
]
|
|
417
|
-
if backoff_on_ratelimit is not None:
|
|
418
|
-
hooks.insert(0, backoff_on_ratelimit)
|
|
419
|
-
return hooks
|
|
@@ -5,6 +5,7 @@ Provides file-based token storage and helpers for building OAuth providers
|
|
|
5
5
|
using the MCP SDK's built-in OAuthClientProvider.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import asyncio
|
|
8
9
|
import json
|
|
9
10
|
import os
|
|
10
11
|
import typing as t
|
|
@@ -64,7 +65,8 @@ class FileTokenStorage:
|
|
|
64
65
|
async def get_tokens(self) -> "OAuthToken | None":
|
|
65
66
|
from mcp.shared.auth import OAuthToken
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
entry = await asyncio.to_thread(self._get_entry)
|
|
69
|
+
data = entry.get("tokens")
|
|
68
70
|
if data is None:
|
|
69
71
|
return None
|
|
70
72
|
try:
|
|
@@ -74,14 +76,18 @@ class FileTokenStorage:
|
|
|
74
76
|
return None
|
|
75
77
|
|
|
76
78
|
async def set_tokens(self, tokens: "OAuthToken") -> None:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
def _update() -> None:
|
|
80
|
+
entry = self._get_entry()
|
|
81
|
+
entry["tokens"] = tokens.model_dump(mode="json", exclude_none=True)
|
|
82
|
+
self._set_entry(entry)
|
|
83
|
+
|
|
84
|
+
await asyncio.to_thread(_update)
|
|
80
85
|
|
|
81
86
|
async def get_client_info(self) -> "OAuthClientInformationFull | None":
|
|
82
87
|
from mcp.shared.auth import OAuthClientInformationFull
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
entry = await asyncio.to_thread(self._get_entry)
|
|
90
|
+
data = entry.get("client_info")
|
|
85
91
|
if data is None:
|
|
86
92
|
return None
|
|
87
93
|
try:
|
|
@@ -91,9 +97,12 @@ class FileTokenStorage:
|
|
|
91
97
|
return None
|
|
92
98
|
|
|
93
99
|
async def set_client_info(self, client_info: "OAuthClientInformationFull") -> None:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
def _update() -> None:
|
|
101
|
+
entry = self._get_entry()
|
|
102
|
+
entry["client_info"] = client_info.model_dump(mode="json", exclude_none=True)
|
|
103
|
+
self._set_entry(entry)
|
|
104
|
+
|
|
105
|
+
await asyncio.to_thread(_update)
|
|
97
106
|
|
|
98
107
|
|
|
99
108
|
async def _default_redirect_handler(url: str) -> None:
|
|
@@ -254,7 +254,7 @@ class MCPClient:
|
|
|
254
254
|
error_msg = f"{error_msg}\n\nServer stderr:\n{stderr_tail}"
|
|
255
255
|
|
|
256
256
|
error_lower = error_msg.lower()
|
|
257
|
-
if
|
|
257
|
+
if any(kw in error_lower for kw in ("unauthorized", "forbidden", "401", "403", "oauth")):
|
|
258
258
|
self._status = MCPStatus.NEEDS_AUTH
|
|
259
259
|
else:
|
|
260
260
|
self._status = MCPStatus.FAILED
|
|
@@ -363,9 +363,9 @@ class MCPClient:
|
|
|
363
363
|
"""Probe whether a URL supports streamable HTTP (POST).
|
|
364
364
|
|
|
365
365
|
Returns True if the server likely supports streamable HTTP, False if it
|
|
366
|
-
clearly doesn't
|
|
367
|
-
|
|
368
|
-
|
|
366
|
+
clearly doesn't. Connectivity errors also return False (server
|
|
367
|
+
unreachable via POST, try SSE). Auth and SSL errors are allowed to
|
|
368
|
+
propagate — they would affect SSE equally.
|
|
369
369
|
"""
|
|
370
370
|
try:
|
|
371
371
|
probe_headers = dict(headers) if headers else {}
|
|
@@ -377,6 +377,16 @@ class MCPClient:
|
|
|
377
377
|
)
|
|
378
378
|
async with httpx.AsyncClient(timeout=min(http_timeout, 5), auth=auth) as probe:
|
|
379
379
|
r = await probe.post(url, content=b"{}", headers=probe_headers)
|
|
380
|
+
# Auth errors affect both transports equally — propagate so
|
|
381
|
+
# callers can classify as NEEDS_AUTH instead of falling through.
|
|
382
|
+
if r.status_code in (401, 403):
|
|
383
|
+
r.raise_for_status()
|
|
384
|
+
if r.status_code == 400 and self._looks_like_jsonrpc_probe_rejection(r):
|
|
385
|
+
logger.debug(
|
|
386
|
+
"Streamable HTTP probe got JSON-RPC validation error for {}, using streamable-http",
|
|
387
|
+
url,
|
|
388
|
+
)
|
|
389
|
+
return True
|
|
380
390
|
# 400 = bad request (SSE-only server doesn't understand POST body)
|
|
381
391
|
# 404 = endpoint not found for POST
|
|
382
392
|
# 405 = method not allowed (explicitly rejects POST)
|
|
@@ -394,6 +404,24 @@ class MCPClient:
|
|
|
394
404
|
|
|
395
405
|
return True
|
|
396
406
|
|
|
407
|
+
@staticmethod
|
|
408
|
+
def _looks_like_jsonrpc_probe_rejection(response: httpx.Response) -> bool:
|
|
409
|
+
"""Detect JSON-RPC validation errors from real MCP streamable-http servers."""
|
|
410
|
+
content_type = response.headers.get("content-type", "").lower()
|
|
411
|
+
if "application/json" not in content_type:
|
|
412
|
+
return False
|
|
413
|
+
|
|
414
|
+
with contextlib.suppress(ValueError):
|
|
415
|
+
payload = response.json()
|
|
416
|
+
if (
|
|
417
|
+
isinstance(payload, dict)
|
|
418
|
+
and payload.get("jsonrpc") == "2.0"
|
|
419
|
+
and isinstance(payload.get("error"), dict)
|
|
420
|
+
):
|
|
421
|
+
return True
|
|
422
|
+
|
|
423
|
+
return False
|
|
424
|
+
|
|
397
425
|
async def _connect_via_streamable_http(self, connection: dict[str, t.Any]) -> "ClientSession":
|
|
398
426
|
"""Connect via streamable HTTP, falling back to SSE on failure."""
|
|
399
427
|
from mcp import ClientSession
|
|
@@ -172,14 +172,18 @@ class SubAgentToolset(Toolset):
|
|
|
172
172
|
sub_agent.reset()
|
|
173
173
|
|
|
174
174
|
try:
|
|
175
|
-
|
|
175
|
+
trajectory = await sub_agent.run(task)
|
|
176
|
+
# Get the last assistant message content as the response
|
|
177
|
+
last_message = trajectory.messages[-1] if trajectory.messages else None
|
|
178
|
+
response = str(last_message.content) if last_message else ""
|
|
179
|
+
|
|
176
180
|
result = f"## Sub-agent: {config['name']}\n\n"
|
|
177
181
|
result += f"**Task:** {task}\n\n"
|
|
178
|
-
result += f"**Steps taken:** {len(
|
|
179
|
-
result += f"**Tokens used:** {
|
|
182
|
+
result += f"**Steps taken:** {len(trajectory.steps)}\n"
|
|
183
|
+
result += f"**Tokens used:** {trajectory.usage.total_tokens}\n\n"
|
|
180
184
|
result += f"**Result:**\n{response}"
|
|
181
185
|
|
|
182
|
-
logger.info(f"Sub-agent completed in {len(
|
|
186
|
+
logger.info(f"Sub-agent completed in {len(trajectory.steps)} steps")
|
|
183
187
|
except Exception as e:
|
|
184
188
|
logger.error(f"Sub-agent failed: {e}")
|
|
185
189
|
return f"Sub-agent failed: {e}"
|
|
@@ -7,6 +7,7 @@ import re
|
|
|
7
7
|
import typing as t
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
+
import aiofiles
|
|
10
11
|
import typing_extensions as te
|
|
11
12
|
from pydantic import (
|
|
12
13
|
BaseModel,
|
|
@@ -162,21 +163,24 @@ class ToolResponse(XMLModel):
|
|
|
162
163
|
result: str
|
|
163
164
|
|
|
164
165
|
|
|
165
|
-
def offload_tool_output(
|
|
166
|
+
async def offload_tool_output(
|
|
166
167
|
content: str,
|
|
167
168
|
tool_call_id: str,
|
|
168
169
|
tool_name: str, # noqa: ARG001 - callers pass as keyword arg
|
|
169
170
|
working_dir: Path,
|
|
170
171
|
) -> tuple[str, Path]:
|
|
171
172
|
"""Write tool output to disk and return middle-out summary plus file path."""
|
|
172
|
-
|
|
173
|
-
output_dir
|
|
173
|
+
output_root = working_dir if working_dir.name == ".dreadnode" else working_dir / ".dreadnode"
|
|
174
|
+
output_dir = output_root / "tool-output"
|
|
175
|
+
await asyncio.to_thread(output_dir.mkdir, parents=True, exist_ok=True)
|
|
174
176
|
|
|
175
177
|
safe_id = re.sub(r"[^a-zA-Z0-9_-]", "_", tool_call_id)
|
|
176
178
|
file_path = output_dir / f"{safe_id}.txt"
|
|
177
|
-
|
|
179
|
+
async with aiofiles.open(file_path, "w") as f:
|
|
180
|
+
await f.write(content)
|
|
178
181
|
|
|
179
|
-
|
|
182
|
+
relative_base = working_dir.parent if working_dir.name == ".dreadnode" else working_dir
|
|
183
|
+
relative_path = file_path.relative_to(relative_base)
|
|
180
184
|
|
|
181
185
|
half = OFFLOAD_THRESHOLD // 2
|
|
182
186
|
start = content[:half]
|
|
@@ -422,7 +426,9 @@ class Tool(BaseModel, t.Generic[P, R]):
|
|
|
422
426
|
else:
|
|
423
427
|
with contextlib.suppress(Exception):
|
|
424
428
|
if type(result) not in [str, int, float, bool]:
|
|
425
|
-
result =
|
|
429
|
+
result = await asyncio.to_thread(
|
|
430
|
+
lambda: TypeAdapter(t.Any).dump_json(result).decode(errors="replace")
|
|
431
|
+
)
|
|
426
432
|
message.content_parts = [ContentText(text=str(result))]
|
|
427
433
|
|
|
428
434
|
if self.truncate:
|
|
@@ -432,7 +438,7 @@ class Tool(BaseModel, t.Generic[P, R]):
|
|
|
432
438
|
|
|
433
439
|
if self.offload and message.content and len(message.content) > OFFLOAD_THRESHOLD:
|
|
434
440
|
working_dir = self.working_dir or Path.cwd()
|
|
435
|
-
truncated_content, output_file = offload_tool_output(
|
|
441
|
+
truncated_content, output_file = await offload_tool_output(
|
|
436
442
|
message.content,
|
|
437
443
|
tool_call.id,
|
|
438
444
|
tool_call.name,
|
|
@@ -297,10 +297,18 @@ def trajectory_from_openai_format(
|
|
|
297
297
|
step_num = 0
|
|
298
298
|
|
|
299
299
|
for msg in messages:
|
|
300
|
-
|
|
301
|
-
role
|
|
302
|
-
content
|
|
303
|
-
|
|
300
|
+
kwargs: dict[str, t.Any] = {
|
|
301
|
+
"role": msg.get("role", "user"),
|
|
302
|
+
"content": msg.get("content", ""),
|
|
303
|
+
}
|
|
304
|
+
# Preserve tool calls (assistant messages) and tool_call_id (tool responses)
|
|
305
|
+
if msg.get("tool_calls"):
|
|
306
|
+
kwargs["tool_calls"] = msg["tool_calls"]
|
|
307
|
+
if msg.get("tool_call_id"):
|
|
308
|
+
kwargs["tool_call_id"] = msg["tool_call_id"]
|
|
309
|
+
if msg.get("metadata"):
|
|
310
|
+
kwargs["metadata"] = msg["metadata"]
|
|
311
|
+
message = message_class(**kwargs)
|
|
304
312
|
current_messages.append(message)
|
|
305
313
|
|
|
306
314
|
# Create a step when we see an assistant message (the natural boundary)
|