dreadnode 2.0.27__tar.gz → 2.0.28__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.27 → dreadnode-2.0.28}/PKG-INFO +1 -1
- dreadnode-2.0.28/dreadnode/agents/mcp/auth.py +486 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/mcp/client.py +135 -12
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/api/models.py +23 -1
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/task.py +13 -1
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/client/runtime_client.py +21 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/main.py +26 -1
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/print_mode.py +7 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/app.py +79 -3
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/capability_manager.py +415 -117
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/app.py +111 -16
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/capabilities_manager.py +17 -4
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/dreadnode.tcss +20 -2
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screen_router.py +8 -2
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/capabilities.py +45 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/services.py +576 -199
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/INDEX.md +0 -1
- dreadnode-2.0.28/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/mcp-servers.md +277 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/writing-skills.md +0 -2
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/sdk/agents.md +3 -1
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/sdk/capabilities.md +30 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/task.md +2 -0
- dreadnode-2.0.28/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/getting-started/quickstart.md +165 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/credits.md +8 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/environment-lifecycle.md +0 -1
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/loader.py +61 -1
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/types.py +77 -2
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/generator/litellm_.py +9 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/packaging/oci.py +23 -3
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/packaging/task_validation.py +38 -9
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/jobs.py +8 -2
- {dreadnode-2.0.27 → dreadnode-2.0.28}/pyproject.toml +2 -1
- dreadnode-2.0.27/dreadnode/agents/mcp/auth.py +0 -149
- dreadnode-2.0.27/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/mcp-servers.md +0 -174
- dreadnode-2.0.27/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/guides/capability-optimization-loop.md +0 -133
- dreadnode-2.0.27/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/getting-started/quickstart.md +0 -134
- {dreadnode-2.0.27 → dreadnode-2.0.28}/.gitignore +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/LICENSE +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/README.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/__main__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/agent.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/events.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/exceptions.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/format.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/hooks.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/judge.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/mcp/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/mcp/config.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/mcp/server.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/process_judge.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/reactions.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/skills.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/stopping.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/subagent.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/tool_resolution.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/tools.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/agents/trajectory.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/adversarial_reasoning.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/advpromptier.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/alignment_faking.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/analogy_escalation.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/analytics/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/analytics/aggregator.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/analytics/classifier.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/analytics/compliance.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/analytics/engine.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/analytics/recommendations.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/analytics/types.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/aprt_progressive.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/assessment.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/attention_shifting.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/autodan_turbo.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/autoredteamer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/beast.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/compliance/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/compliance/atlas.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/compliance/nist.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/compliance/owasp.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/compliance/owasp_agentic.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/compliance/saif.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/constants.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/cot_jailbreak.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/crescendo.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/assets/audio/adversarial_query.mp3 +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/assets/image/bomb.jpg +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/assets/image/meth.png +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/prompts/adversarial_benchmark_subset.csv +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/prompts/ai_safety.csv +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/data_exfiltration.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/goal_hijacking.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/idor.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/memory_poisoning.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/privilege_escalation.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/rce.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/scope_creep.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/tool_chaining.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/tool_selection_safety.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/unbounded_agency.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/rubrics/web_chatbot_security.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/templates/crescendo/variant_1.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/templates/crescendo/variant_2.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/templates/crescendo/variant_3.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/templates/crescendo/variant_4.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/data/templates/crescendo/variant_5.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/deep_inception.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/drattack.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/echo_chamber.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/events.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/genetic_persona.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/goat.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/goat_v2.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/gptfuzzer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/humor_bypass.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/image.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/j2_meta.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/jbdistill.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/jbfuzz.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/lrm_autonomous.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/mapf.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/multimodal.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/nexus.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/pair.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/persona_hijack.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/prompt.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/quantization_safety.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/rainbow.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/refusal_aware.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/renellm.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/reporting/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/reporting/json_report.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/reporting/llm_summary.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/reporting/markdown.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/reward_hacking.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/salami_slicing.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/self_persuasion.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/siren.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/tap.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/templatefuzz.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/tmap_trajectory.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/trojail.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/airt/watermark_removal.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/api/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/api/client.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/airt.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/args.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/capability.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/dataset.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/environment.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/evaluation.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/inference_model.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/judge.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/main.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/model.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/optimize.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/runtime.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/sandbox.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/secret.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/session.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/shared.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/challenge/Dockerfile +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/docker-compose.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/provision.sh +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/solution.sh +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/task-remote.yaml.tmpl +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/task.yaml.tmpl +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/teardown.sh +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/templates/init/verify.sh +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/train.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/cli/worlds.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/client/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/client/interactive.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/client/managed_client.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/client/models.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/client/transports.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/config.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/env.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/model_catalog.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/paths.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/auth.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/model_resolution.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/prompt.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/prompt_registry.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/runtime_events.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/session_hydrator.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/session_persistence.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/turn_coordinator.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/utils.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/websocket.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/server/worker_manager.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/auth_flow.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/command_dispatcher.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/commands.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/connection.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/error_handler.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/model_manager.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/model_variants.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/profile_manager.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/runtime_cache.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/auth.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/base.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/capability_docs.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/connection_error.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/console.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/environments.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/evaluations.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/model_picker.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/models.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/raw_spans.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/runtimes.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/sandboxes.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/secrets.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/sessions.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/theme_showcase.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/traces.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/screens/workspaces.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/sessions_manager.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/spans_reader.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/status_messages.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/theme.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/tool_format.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/turn_coordinator.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/turn_lifecycle.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/turn_reducer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/turn_state_phase.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/update_check.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/agent_dialog.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/agent_suggester.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/composer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/confirm_modal.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/context_bar.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/conversation.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/flash.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/header_bar.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/help_panel.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/human_prompt.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/mention_overlay.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/message_queue.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/new_messages_pill.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/overlay_mixin.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/permission_prompt.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/profile_dialog.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/prompt_info.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/rewind_picker.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/session_sidebar.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/skills_dialog.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/slash_overlay.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/status_bar.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/throbber.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/tool.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/tool_progress.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/tools_dialog.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/welcome.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/widgets/whoami.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/app/tui/wire_events.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/MAINTAINING.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/agents/dreadnode.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/capability.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/SKILL.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/capability-improvement.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/methodology-grounding.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/prior-art-audit.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/promoting-capabilities.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/quality-bar.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/agents.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/dependencies-and-checks.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/env-vars.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/events.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/flags.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/hooks.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/installing.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/manifest.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/overview.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/policies.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/publishing.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/quickstart.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/skills.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/tools.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/workers-reference.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/capabilities/workers.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references/tui/default-tools.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/references-and-depth.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/runtime-default-capability.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/SKILL.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/INDEX.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/airt.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/capability.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/dataset.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/env.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/evaluation.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/inference-model.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/main.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/model.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/optimize.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/overview.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/runtime.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/sandbox.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/secret.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/train.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/worlds.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/getting-started/authentication.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/SKILL.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/INDEX.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/capabilities/installing.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/capabilities/overview.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/getting-started/authentication.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/getting-started/overview.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/chat-models.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/organizations.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/overview.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/projects.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/secrets.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/settings.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/users.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/workspaces.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/configuration.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/managing.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/manifest-reference.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/overview.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/quickstart.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/serve.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/inspecting.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/overview.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/getting-started/SKILL.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/dreadnode/skills/research-capabilities/SKILL.md +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/.gitignore +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/capability.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/hooks/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/hooks/observer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/self_improvement_lib/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/self_improvement_lib/classifier.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/self_improvement_lib/reflector_goal.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/self_improvement_lib/skill_io.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/skills-promoted/.gitkeep +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/skills-proposed/.gitkeep +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/tools/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/tools/reflector.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/builtin_capabilities/self-improvement/tools/skill_stats.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/capability.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/flags.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/install.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/sync.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/tool_rules.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/worker.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/capabilities/worker_runner.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/conditions.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/discovery.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/environment.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/exceptions.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/execution.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/hook.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/judge.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/load.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/log.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/meta/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/meta/config.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/meta/context.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/meta/hydrate.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/meta/introspect.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/metric.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/object.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/scorer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/serialization.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/stopping.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/task.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/templating.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/transforms.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/audio.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/base.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/common.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/image.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/object_3d.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/table.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/text.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/types/video.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/core/util.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/datasets/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/datasets/dataset.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/datasets/hf.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/datasets/local.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/evaluations/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/evaluations/console.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/evaluations/evaluation.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/evaluations/events.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/evaluations/format.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/evaluations/result.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/evaluations/sample.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/caching.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/chat.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/data.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/exceptions.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/generator/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/generator/base.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/generator/http.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/generator/transformers_.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/generator/vllm_.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/message.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/models.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/parsing.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/tokenizer/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/tokenizer/base.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/tokenizer/transformers_.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/generators/utils.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/judges/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/judges/outcome.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/judges/trajectory.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/judges/trajectory_tools.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/models/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/models/hf.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/models/local.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/models/model.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/adapters/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/adapters/_env_eval.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/adapters/agent.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/adapters/env.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/adapters/runtime.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/adapters/stack.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/api.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/backends/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/backends/base.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/backends/gepa.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/collectors.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/config.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/console.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/events.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/format.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/jobs.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/result.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/sampler.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/sampling.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/search.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/stopping.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/study.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/optimization/trial.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/packaging/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/packaging/loader.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/packaging/manifest.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/packaging/package.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/policies/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/policies/guard.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/policies/rubrics/process/default.yaml +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/py.typed +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/boundary.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/fuzzing.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/graph.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/grid.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/image.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/mapelites.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/optuna.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/random.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/registry.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/samplers/strategy.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/advanced_jailbreak_detection.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/agent_security.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/agentic.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/agentic_workflow.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/attack_outcome.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/classification.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/consistency.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/contains.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/cosine_sim.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/credentials.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/crucible.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/documentation_security.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/exfiltration_detection.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/format.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/harm.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/ide_security.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/image.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/json.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/judge.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/judge_ensemble.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/length.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/lexical.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/mcp_security.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/memorization.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/multi_agent_security.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/pii.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/prompt_leak.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/readability.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/reasoning_security.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/sentiment.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/similarity.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/structural_detection.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/scorers/supply_chain_detection.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/storage/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/storage/providers.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/storage/session_store.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/storage/storage.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/_ripgrep.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/apply_patch.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/dreadnode_cli.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/editing.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/execute.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/fetch.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/glob.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/grep.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/interaction.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/ls.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/memory.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/read.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/report.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/task.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/think.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/todo.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/trajectory_search.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/web_extract.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/web_search.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tools/write.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tracing/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tracing/constants.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tracing/convert.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tracing/exporter.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tracing/exporters.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tracing/span.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tracing/spans.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/tracing/trace_converter.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/_progress.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/base.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/dpo.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/env_rollouts.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/etl/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/etl/_common.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/etl/rl.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/etl/sft.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/etl/worlds.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/events.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/grpo.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/models.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ppo.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/prime.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/async_trainer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/config.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/coordinator.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/distributed.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/dpo.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/experience.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/fsdp2_learner.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/inference.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/learner.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/multi_turn.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/ppo.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/reward_model.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/rollout_env.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/rollout_worker.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/sft.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/ray/trainer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/recipes.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rewards/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rewards/aggregator.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rewards/functions.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rewards/scorer_bridge.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rewards/shaping.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rewards/types.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rollouts/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rollouts/adapters.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rollouts/agent_rollout.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rollouts/orchestrator.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rollouts/types.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/rollouts/worlds.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/serving/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/serving/vllm_client.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/sft.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/tinker/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/tinker/config.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/tinker/data.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/tinker/renderer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/tinker/rl.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/tinker/trainer.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/tinker_sft.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/training/utils.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/__init__.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/advanced_jailbreak.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/adversarial_suffix.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/agent_skill.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/agentic_workflow.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/audio.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/backdoor_finetune.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/browser_agent_attacks.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/cipher.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/competitive_parity.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/constitutional.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/document.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/documentation_poison.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/encoding.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/exfiltration.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/flip_attack.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/guardrail_bypass.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/ide_injection.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/image.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/injection.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/json_tools.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/language.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/logic_bomb.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/mcp_attacks.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/multi_agent_attacks.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/multimodal_attacks.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/persuasion.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/perturbation.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/pii_extraction.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/pythonic_tools.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/rag_poisoning.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/reasoning_attacks.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/refine.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/response_steering.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/structural_exploits.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/stylistic.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/substitution.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/supply_chain.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/swap.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/system_prompt_extraction.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/text.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/video.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/transforms/xml_tools.py +0 -0
- {dreadnode-2.0.27 → dreadnode-2.0.28}/dreadnode/version.py +0 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP OAuth authentication support.
|
|
3
|
+
|
|
4
|
+
Provides file-based token storage, a localhost callback server for catching
|
|
5
|
+
OAuth redirects, a browser-opening redirect handler, and a factory that wires
|
|
6
|
+
those defaults onto the MCP SDK's :class:`OAuthClientProvider`.
|
|
7
|
+
|
|
8
|
+
Together they enable native HTTP MCP servers (e.g. Linear, Atlassian) to be
|
|
9
|
+
authenticated end-to-end by the Dreadnode runtime without going through the
|
|
10
|
+
``npx mcp-remote`` stdio bridge.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import contextlib
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import socket
|
|
18
|
+
import sys
|
|
19
|
+
import typing as t
|
|
20
|
+
import webbrowser
|
|
21
|
+
from collections.abc import Awaitable, Callable
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from urllib.parse import parse_qs, urlparse
|
|
24
|
+
|
|
25
|
+
from loguru import logger
|
|
26
|
+
|
|
27
|
+
if t.TYPE_CHECKING:
|
|
28
|
+
from mcp.client.auth.oauth2 import OAuthClientProvider, TokenStorage
|
|
29
|
+
from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
|
|
30
|
+
|
|
31
|
+
from dreadnode.agents.mcp.config import OAuthConfig
|
|
32
|
+
|
|
33
|
+
DEFAULT_AUTH_PATH = Path.home() / ".dreadnode" / "mcp-auth.json"
|
|
34
|
+
|
|
35
|
+
# Default time (seconds) the local callback server waits for the user to
|
|
36
|
+
# complete the OAuth flow in their browser before giving up.
|
|
37
|
+
_DEFAULT_CALLBACK_TIMEOUT = 300.0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MCPOAuthRequiredError(Exception):
|
|
41
|
+
"""Raised when a connect would need to open a browser to complete OAuth
|
|
42
|
+
but the runtime isn't allowed to (a non-interactive / background connect).
|
|
43
|
+
|
|
44
|
+
Signals the lifecycle to classify the server as ``needs_auth`` and defer
|
|
45
|
+
the browser-open to a user-initiated Authenticate, rather than popping a
|
|
46
|
+
window during startup (CAP-MCP-010 — the runtime owns the browser-open
|
|
47
|
+
moment).
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class FileTokenStorage:
|
|
52
|
+
"""Persist OAuth tokens to disk, keyed by server URL.
|
|
53
|
+
|
|
54
|
+
File format (at ~/.dreadnode/mcp-auth.json, mode 0o600):
|
|
55
|
+
{
|
|
56
|
+
"https://api.example.com/mcp": {
|
|
57
|
+
"tokens": { ... },
|
|
58
|
+
"client_info": { ... }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, server_url: str, path: Path | None = None) -> None:
|
|
64
|
+
self._server_url = server_url
|
|
65
|
+
self._path = path or DEFAULT_AUTH_PATH
|
|
66
|
+
|
|
67
|
+
def _read_store(self) -> dict[str, t.Any]:
|
|
68
|
+
if not self._path.exists():
|
|
69
|
+
return {}
|
|
70
|
+
try:
|
|
71
|
+
return json.loads(self._path.read_text())
|
|
72
|
+
except (json.JSONDecodeError, OSError):
|
|
73
|
+
logger.warning("Corrupt MCP auth file at {}, starting fresh", self._path)
|
|
74
|
+
return {}
|
|
75
|
+
|
|
76
|
+
def _write_store(self, store: dict[str, t.Any]) -> None:
|
|
77
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
fd = os.open(self._path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
79
|
+
with os.fdopen(fd, "w") as f:
|
|
80
|
+
json.dump(store, f, indent=2)
|
|
81
|
+
|
|
82
|
+
def _get_entry(self) -> dict[str, t.Any]:
|
|
83
|
+
return self._read_store().get(self._server_url, {})
|
|
84
|
+
|
|
85
|
+
def has_tokens(self) -> bool:
|
|
86
|
+
"""Synchronously report whether this server has stored OAuth tokens.
|
|
87
|
+
|
|
88
|
+
Used by the runtime to decide, *before* a background connect, whether
|
|
89
|
+
to wire up an OAuth provider at all. With no stored token a background
|
|
90
|
+
connect attaches no provider — a 401 then surfaces as ``needs_auth``
|
|
91
|
+
without any discovery/DCR traffic or browser-open. A small local file
|
|
92
|
+
read is cheap enough to do inline on the connect path.
|
|
93
|
+
"""
|
|
94
|
+
return bool(self._get_entry().get("tokens"))
|
|
95
|
+
|
|
96
|
+
def _set_entry(self, entry: dict[str, t.Any]) -> None:
|
|
97
|
+
store = self._read_store()
|
|
98
|
+
store[self._server_url] = entry
|
|
99
|
+
self._write_store(store)
|
|
100
|
+
|
|
101
|
+
async def get_tokens(self) -> "OAuthToken | None":
|
|
102
|
+
from mcp.shared.auth import OAuthToken
|
|
103
|
+
|
|
104
|
+
entry = await asyncio.to_thread(self._get_entry)
|
|
105
|
+
data = entry.get("tokens")
|
|
106
|
+
if data is None:
|
|
107
|
+
return None
|
|
108
|
+
try:
|
|
109
|
+
return OAuthToken.model_validate(data)
|
|
110
|
+
except Exception:
|
|
111
|
+
logger.warning("Invalid stored tokens for {}", self._server_url)
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
async def set_tokens(self, tokens: "OAuthToken") -> None:
|
|
115
|
+
def _update() -> None:
|
|
116
|
+
entry = self._get_entry()
|
|
117
|
+
entry["tokens"] = tokens.model_dump(mode="json", exclude_none=True)
|
|
118
|
+
self._set_entry(entry)
|
|
119
|
+
|
|
120
|
+
await asyncio.to_thread(_update)
|
|
121
|
+
|
|
122
|
+
async def get_client_info(self) -> "OAuthClientInformationFull | None":
|
|
123
|
+
from mcp.shared.auth import OAuthClientInformationFull
|
|
124
|
+
|
|
125
|
+
entry = await asyncio.to_thread(self._get_entry)
|
|
126
|
+
data = entry.get("client_info")
|
|
127
|
+
if data is None:
|
|
128
|
+
return None
|
|
129
|
+
try:
|
|
130
|
+
return OAuthClientInformationFull.model_validate(data)
|
|
131
|
+
except Exception:
|
|
132
|
+
logger.warning("Invalid stored client info for {}", self._server_url)
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
async def set_client_info(self, client_info: "OAuthClientInformationFull") -> None:
|
|
136
|
+
def _update() -> None:
|
|
137
|
+
entry = self._get_entry()
|
|
138
|
+
entry["client_info"] = client_info.model_dump(mode="json", exclude_none=True)
|
|
139
|
+
self._set_entry(entry)
|
|
140
|
+
|
|
141
|
+
await asyncio.to_thread(_update)
|
|
142
|
+
|
|
143
|
+
async def clear(self) -> None:
|
|
144
|
+
"""Remove this server's stored tokens + client_info from the file.
|
|
145
|
+
|
|
146
|
+
Targeted by server URL — other servers' entries are untouched.
|
|
147
|
+
Used by the user-initiated re-authenticate flow so a fresh OAuth
|
|
148
|
+
round-trip runs on the next connect without wiping the whole
|
|
149
|
+
cache (other authenticated capabilities keep working).
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def _clear() -> None:
|
|
153
|
+
store = self._read_store()
|
|
154
|
+
if self._server_url in store:
|
|
155
|
+
del store[self._server_url]
|
|
156
|
+
self._write_store(store)
|
|
157
|
+
|
|
158
|
+
await asyncio.to_thread(_clear)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# --- Localhost OAuth callback server -----------------------------------------
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
_SUCCESS_HTML = (
|
|
165
|
+
b"<!doctype html><html><head><meta charset='utf-8'>"
|
|
166
|
+
b"<title>Dreadnode \xe2\x80\x94 Authentication complete</title>"
|
|
167
|
+
b"<style>body{font:14px/1.5 system-ui,sans-serif;max-width:32rem;"
|
|
168
|
+
b"margin:6rem auto;padding:0 1rem;color:#222;}h2{margin-bottom:.5rem}</style>"
|
|
169
|
+
b"</head><body><h2>Authentication complete \xe2\x9c\x93</h2>"
|
|
170
|
+
b"<p>You can close this tab and return to the Dreadnode TUI.</p>"
|
|
171
|
+
b"</body></html>"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
_ERROR_HTML = (
|
|
175
|
+
b"<!doctype html><html><head><meta charset='utf-8'>"
|
|
176
|
+
b"<title>Dreadnode \xe2\x80\x94 Authentication failed</title></head>"
|
|
177
|
+
b"<body><h2>Authentication failed</h2>"
|
|
178
|
+
b"<p>The authorization server didn't return a code parameter. "
|
|
179
|
+
b"Return to the Dreadnode TUI and retry.</p></body></html>"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class LocalCallbackServer:
|
|
184
|
+
"""One-shot HTTP server on a 127.0.0.1 ephemeral port for OAuth redirects.
|
|
185
|
+
|
|
186
|
+
Binds a socket eagerly in ``__init__`` so ``redirect_uri`` is known
|
|
187
|
+
before the OAuth flow needs it (the authorization server has to be
|
|
188
|
+
told the redirect URI at the start of the authorization request, well
|
|
189
|
+
before the user actually completes the flow). ``start()`` converts
|
|
190
|
+
the bound socket into a running ``asyncio.Server``; ``wait_for_callback()``
|
|
191
|
+
blocks until the redirect arrives, then tears the server down.
|
|
192
|
+
|
|
193
|
+
Single-use by design — a fresh instance per OAuth flow, no recycling.
|
|
194
|
+
The redirect URI is always ``http://127.0.0.1:<port>/callback``.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def __init__(
|
|
198
|
+
self,
|
|
199
|
+
*,
|
|
200
|
+
host: str = "127.0.0.1",
|
|
201
|
+
timeout: float = _DEFAULT_CALLBACK_TIMEOUT,
|
|
202
|
+
) -> None:
|
|
203
|
+
self._host = host
|
|
204
|
+
self._timeout = timeout
|
|
205
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
206
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
207
|
+
sock.bind((host, 0))
|
|
208
|
+
self._socket: socket.socket | None = sock
|
|
209
|
+
self._port: int = sock.getsockname()[1]
|
|
210
|
+
self._result: tuple[str, str | None] | None = None
|
|
211
|
+
self._error: str | None = None
|
|
212
|
+
self._received = asyncio.Event()
|
|
213
|
+
self._server: asyncio.Server | None = None
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def port(self) -> int:
|
|
217
|
+
return self._port
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def redirect_uri(self) -> str:
|
|
221
|
+
return f"http://{self._host}:{self._port}/callback"
|
|
222
|
+
|
|
223
|
+
async def start(self) -> None:
|
|
224
|
+
"""Begin listening for the OAuth callback. Idempotent."""
|
|
225
|
+
if self._server is not None:
|
|
226
|
+
return
|
|
227
|
+
if self._socket is None:
|
|
228
|
+
msg = "LocalCallbackServer socket is already released — cannot start"
|
|
229
|
+
raise RuntimeError(msg)
|
|
230
|
+
self._server = await asyncio.start_server(self._handle_request, sock=self._socket)
|
|
231
|
+
# Server now owns the socket.
|
|
232
|
+
self._socket = None
|
|
233
|
+
logger.debug("OAuth callback server listening on {}", self.redirect_uri)
|
|
234
|
+
|
|
235
|
+
async def _handle_request(
|
|
236
|
+
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
|
237
|
+
) -> None:
|
|
238
|
+
try:
|
|
239
|
+
request_line = await reader.readline()
|
|
240
|
+
line = request_line.decode("latin-1", errors="replace").strip()
|
|
241
|
+
# Drain headers — we only need the request line.
|
|
242
|
+
while True:
|
|
243
|
+
hdr = await reader.readline()
|
|
244
|
+
if not hdr or hdr in (b"\r\n", b"\n"):
|
|
245
|
+
break
|
|
246
|
+
|
|
247
|
+
_method, _, rest = line.partition(" ")
|
|
248
|
+
target, _, _ = rest.partition(" ")
|
|
249
|
+
parsed = urlparse(target)
|
|
250
|
+
params = parse_qs(parsed.query)
|
|
251
|
+
code = params.get("code", [""])[0]
|
|
252
|
+
state_list = params.get("state")
|
|
253
|
+
state = state_list[0] if state_list else None
|
|
254
|
+
err = params.get("error", [""])[0]
|
|
255
|
+
err_desc = params.get("error_description", [""])[0]
|
|
256
|
+
|
|
257
|
+
if code:
|
|
258
|
+
self._result = (code, state)
|
|
259
|
+
self._received.set()
|
|
260
|
+
self._respond(writer, b"200 OK", _SUCCESS_HTML)
|
|
261
|
+
else:
|
|
262
|
+
self._error = err_desc or err or "missing 'code' parameter"
|
|
263
|
+
logger.warning("OAuth callback returned error: {}", self._error)
|
|
264
|
+
self._respond(writer, b"400 Bad Request", _ERROR_HTML)
|
|
265
|
+
self._received.set()
|
|
266
|
+
except Exception as exc:
|
|
267
|
+
logger.warning("OAuth callback handler error: {}", exc)
|
|
268
|
+
self._error = f"callback handler error: {exc}"
|
|
269
|
+
self._received.set()
|
|
270
|
+
finally:
|
|
271
|
+
with contextlib.suppress(Exception):
|
|
272
|
+
writer.close()
|
|
273
|
+
await writer.wait_closed()
|
|
274
|
+
|
|
275
|
+
@staticmethod
|
|
276
|
+
def _respond(writer: asyncio.StreamWriter, status: bytes, body: bytes) -> None:
|
|
277
|
+
writer.write(b"HTTP/1.1 " + status + b"\r\n")
|
|
278
|
+
writer.write(b"Content-Type: text/html; charset=utf-8\r\n")
|
|
279
|
+
writer.write(f"Content-Length: {len(body)}\r\n".encode())
|
|
280
|
+
writer.write(b"Connection: close\r\n\r\n")
|
|
281
|
+
writer.write(body)
|
|
282
|
+
|
|
283
|
+
async def wait_for_callback(self) -> tuple[str, str | None]:
|
|
284
|
+
"""Block until the OAuth callback arrives. Returns (code, state).
|
|
285
|
+
|
|
286
|
+
Raises ``TimeoutError`` if the user doesn't complete the flow
|
|
287
|
+
within the configured timeout, or ``RuntimeError`` if the
|
|
288
|
+
callback arrived with an error parameter instead of a code.
|
|
289
|
+
Always tears the server down before returning.
|
|
290
|
+
"""
|
|
291
|
+
try:
|
|
292
|
+
await asyncio.wait_for(self._received.wait(), timeout=self._timeout)
|
|
293
|
+
finally:
|
|
294
|
+
await self.aclose()
|
|
295
|
+
if self._result is not None:
|
|
296
|
+
return self._result
|
|
297
|
+
err = self._error or "no callback received"
|
|
298
|
+
raise RuntimeError(f"OAuth callback failed: {err}")
|
|
299
|
+
|
|
300
|
+
async def aclose(self) -> None:
|
|
301
|
+
"""Shut down the server and release the socket. Idempotent."""
|
|
302
|
+
if self._server is not None:
|
|
303
|
+
self._server.close()
|
|
304
|
+
with contextlib.suppress(Exception):
|
|
305
|
+
await self._server.wait_closed()
|
|
306
|
+
self._server = None
|
|
307
|
+
if self._socket is not None:
|
|
308
|
+
with contextlib.suppress(Exception):
|
|
309
|
+
self._socket.close()
|
|
310
|
+
self._socket = None
|
|
311
|
+
|
|
312
|
+
def __del__(self) -> None: # pragma: no cover - best-effort GC cleanup
|
|
313
|
+
# Close the held socket if start() was never called (e.g. the
|
|
314
|
+
# OAuth flow never actually needed auth because a cached token
|
|
315
|
+
# was used). asyncio.Server cleanup is async and not reachable
|
|
316
|
+
# from __del__; rely on the loop's own teardown there.
|
|
317
|
+
if self._socket is not None:
|
|
318
|
+
with contextlib.suppress(Exception):
|
|
319
|
+
self._socket.close()
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# --- Redirect handler --------------------------------------------------------
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _is_headless() -> bool:
|
|
326
|
+
"""Best-effort detection of environments where opening a browser fails.
|
|
327
|
+
|
|
328
|
+
Honors the ``DREADNODE_HEADLESS`` opt-out for users who'd prefer to
|
|
329
|
+
complete OAuth manually even on a desktop machine. On Linux,
|
|
330
|
+
absence of ``DISPLAY`` and ``WAYLAND_DISPLAY`` is taken as a strong
|
|
331
|
+
signal (SSH, container, headless CI). On macOS/Windows we trust
|
|
332
|
+
``webbrowser.open``'s return value instead.
|
|
333
|
+
"""
|
|
334
|
+
if os.environ.get("DREADNODE_HEADLESS", "").strip().lower() in ("1", "true", "yes", "on"):
|
|
335
|
+
return True
|
|
336
|
+
if sys.platform.startswith("linux"):
|
|
337
|
+
if not os.environ.get("DISPLAY") and not os.environ.get("WAYLAND_DISPLAY"):
|
|
338
|
+
return True
|
|
339
|
+
return False
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
async def _browser_open_redirect_handler(url: str) -> None:
|
|
343
|
+
"""Open the user's browser to *url*, or log it prominently if we can't.
|
|
344
|
+
|
|
345
|
+
The localhost callback server is the user's only way to deliver the
|
|
346
|
+
OAuth code back to us, so this handler must succeed *or* surface a
|
|
347
|
+
very clear manual fallback. ``DREADNODE_HEADLESS=1`` forces the
|
|
348
|
+
fallback path even on a desktop, which is useful for SSH sessions
|
|
349
|
+
that forward a port but no display.
|
|
350
|
+
"""
|
|
351
|
+
if _is_headless():
|
|
352
|
+
logger.warning(
|
|
353
|
+
"MCP OAuth: browser disabled (DREADNODE_HEADLESS or no display). "
|
|
354
|
+
"Visit this URL to authorize:\n {}",
|
|
355
|
+
url,
|
|
356
|
+
)
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
opened = webbrowser.open(url, new=1, autoraise=True)
|
|
361
|
+
except Exception as exc:
|
|
362
|
+
logger.debug("webbrowser.open raised: {}", exc)
|
|
363
|
+
opened = False
|
|
364
|
+
|
|
365
|
+
if opened:
|
|
366
|
+
logger.info("MCP OAuth: opened browser for authorization. Complete the flow to continue.")
|
|
367
|
+
else:
|
|
368
|
+
logger.warning(
|
|
369
|
+
"MCP OAuth: could not open a browser. Visit this URL manually:\n {}",
|
|
370
|
+
url,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
async def _default_redirect_handler(url: str) -> None:
|
|
375
|
+
"""Backwards-compat shim — log-only handler, kept for external callers
|
|
376
|
+
that explicitly pass it. New defaults use ``_browser_open_redirect_handler``.
|
|
377
|
+
"""
|
|
378
|
+
logger.info("MCP OAuth: Visit this URL to authorize:\n {}", url)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
async def _deferred_redirect_handler(url: str) -> None:
|
|
382
|
+
"""Non-interactive redirect handler: refuse to open a browser.
|
|
383
|
+
|
|
384
|
+
Used by background connects (CAP-MCP-010). When the OAuth flow reaches
|
|
385
|
+
the point of opening the authorization URL, we raise instead — the
|
|
386
|
+
lifecycle classifies the server ``needs_auth`` and the user opens the
|
|
387
|
+
browser later via an explicit Authenticate. The URL is logged at debug
|
|
388
|
+
so it's recoverable for diagnostics, never auto-opened.
|
|
389
|
+
"""
|
|
390
|
+
logger.debug("MCP OAuth: authorization required (deferred, non-interactive): {}", url)
|
|
391
|
+
raise MCPOAuthRequiredError(f"OAuth authorization required for {url}")
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
async def _deferred_callback_handler() -> tuple[str, str | None]:
|
|
395
|
+
"""Non-interactive callback handler — never reached.
|
|
396
|
+
|
|
397
|
+
The deferred redirect handler raises before any callback is awaited;
|
|
398
|
+
this exists only to satisfy the provider's handler-pair contract.
|
|
399
|
+
"""
|
|
400
|
+
raise MCPOAuthRequiredError("OAuth authorization required (non-interactive)")
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def create_oauth_provider(
|
|
404
|
+
server_url: str,
|
|
405
|
+
config: OAuthConfig | None = None,
|
|
406
|
+
storage: "TokenStorage | None" = None,
|
|
407
|
+
redirect_handler: Callable[[str], Awaitable[None]] | None = None,
|
|
408
|
+
callback_handler: Callable[[], Awaitable[tuple[str, str | None]]] | None = None,
|
|
409
|
+
*,
|
|
410
|
+
interactive: bool = True,
|
|
411
|
+
callback_server: LocalCallbackServer | None = None,
|
|
412
|
+
) -> "OAuthClientProvider":
|
|
413
|
+
"""Build an :class:`OAuthClientProvider` wired with working defaults.
|
|
414
|
+
|
|
415
|
+
If neither ``redirect_handler`` nor ``callback_handler`` is provided,
|
|
416
|
+
the function wires the pair according to ``interactive``:
|
|
417
|
+
|
|
418
|
+
- ``interactive=True`` (user-initiated): spin up a
|
|
419
|
+
:class:`LocalCallbackServer`, open the user's browser (falling back to
|
|
420
|
+
a logged URL when headless), and block on the callback server until the
|
|
421
|
+
redirect arrives. The OAuth flow completes end-to-end.
|
|
422
|
+
- ``interactive=False`` (background/startup): never open a browser. A
|
|
423
|
+
valid stored token is still used/refreshed transparently; if the flow
|
|
424
|
+
would actually need to authorize, the deferred redirect handler raises
|
|
425
|
+
:class:`MCPOAuthRequiredError` so the runtime classifies ``needs_auth`` and
|
|
426
|
+
defers the browser to a user-initiated Authenticate (CAP-MCP-010).
|
|
427
|
+
|
|
428
|
+
Passing a custom ``redirect_handler`` *and* ``callback_handler``
|
|
429
|
+
bypasses the defaults entirely — useful for custom UIs (in-TUI
|
|
430
|
+
overlay, IDE picker, etc.).
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
server_url: The MCP server URL.
|
|
434
|
+
config: OAuth configuration. Uses defaults if None.
|
|
435
|
+
storage: Token storage. Defaults to :class:`FileTokenStorage`.
|
|
436
|
+
redirect_handler: Called with the authorization URL. Defaults per
|
|
437
|
+
``interactive`` (browser-open, or deferred raise).
|
|
438
|
+
callback_handler: Called to retrieve ``(code, state)`` from the
|
|
439
|
+
redirect. Defaults to blocking on a localhost callback server.
|
|
440
|
+
interactive: Whether this connect may open a browser. Defaults True.
|
|
441
|
+
callback_server: Inject a pre-built callback server (testing).
|
|
442
|
+
"""
|
|
443
|
+
from mcp.client.auth.oauth2 import OAuthClientProvider
|
|
444
|
+
from mcp.shared.auth import OAuthClientMetadata
|
|
445
|
+
|
|
446
|
+
config = config or OAuthConfig()
|
|
447
|
+
|
|
448
|
+
if storage is None:
|
|
449
|
+
storage = FileTokenStorage(server_url)
|
|
450
|
+
|
|
451
|
+
# If the caller didn't fully customize the handlers, set up the default
|
|
452
|
+
# pair. We do this even when only one side is overridden — that path is
|
|
453
|
+
# symmetric, both halves must come from the caller.
|
|
454
|
+
redirect_uris: list[t.Any] | None = None
|
|
455
|
+
if redirect_handler is None and callback_handler is None:
|
|
456
|
+
if not interactive:
|
|
457
|
+
# Background connect: stored tokens still work, but never pop a
|
|
458
|
+
# browser. The deferred handler raises if authorization is needed.
|
|
459
|
+
redirect_handler = _deferred_redirect_handler
|
|
460
|
+
callback_handler = _deferred_callback_handler
|
|
461
|
+
else:
|
|
462
|
+
if callback_server is None:
|
|
463
|
+
callback_server = LocalCallbackServer()
|
|
464
|
+
captured_server = callback_server # bind for closures + type narrowing
|
|
465
|
+
|
|
466
|
+
async def _redirect(url: str) -> None:
|
|
467
|
+
await captured_server.start()
|
|
468
|
+
await _browser_open_redirect_handler(url)
|
|
469
|
+
|
|
470
|
+
redirect_handler = _redirect
|
|
471
|
+
callback_handler = captured_server.wait_for_callback
|
|
472
|
+
redirect_uris = [captured_server.redirect_uri]
|
|
473
|
+
|
|
474
|
+
client_metadata = OAuthClientMetadata(
|
|
475
|
+
redirect_uris=redirect_uris,
|
|
476
|
+
client_name=config.client_name,
|
|
477
|
+
scope=config.scope,
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
return OAuthClientProvider(
|
|
481
|
+
server_url=server_url,
|
|
482
|
+
client_metadata=client_metadata,
|
|
483
|
+
storage=storage,
|
|
484
|
+
redirect_handler=redirect_handler or _default_redirect_handler,
|
|
485
|
+
callback_handler=callback_handler,
|
|
486
|
+
)
|