dreadnode 2.0.10__tar.gz → 2.0.11__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.10 → dreadnode-2.0.11}/PKG-INFO +1 -1
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/agent.py +131 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/events.py +27 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/hooks.py +4 -75
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/evaluation.py +24 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/shared.py +1 -1
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/app.py +26 -3
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/event_contract.py +9 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/auth.py +6 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/runtimes.py +89 -18
- {dreadnode-2.0.10 → dreadnode-2.0.11}/pyproject.toml +1 -1
- {dreadnode-2.0.10 → dreadnode-2.0.11}/.gitignore +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/LICENSE +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/README.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/__main__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/exceptions.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/format.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/mcp/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/mcp/auth.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/mcp/client.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/mcp/config.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/mcp/server.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/reactions.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/skills.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/stopping.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/subagent.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/tools.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/agents/trajectory.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/analytics/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/analytics/aggregator.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/analytics/classifier.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/analytics/compliance.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/analytics/engine.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/analytics/recommendations.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/analytics/types.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/assessment.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/autodan_turbo.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/beast.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/compliance/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/compliance/atlas.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/compliance/nist.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/compliance/owasp.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/compliance/owasp_agentic.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/compliance/saif.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/constants.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/crescendo.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/assets/audio/adversarial_query.mp3 +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/assets/image/bomb.jpg +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/assets/image/meth.png +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/prompts/adversarial_benchmark_subset.csv +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/prompts/ai_safety.csv +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/data_exfiltration.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/goal_hijacking.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/memory_poisoning.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/privilege_escalation.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/rce.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/scope_creep.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/tool_chaining.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/tool_selection_safety.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/unbounded_agency.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/rubrics/web_chatbot_security.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/templates/crescendo/variant_1.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/templates/crescendo/variant_2.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/templates/crescendo/variant_3.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/templates/crescendo/variant_4.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/data/templates/crescendo/variant_5.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/deep_inception.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/drattack.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/events.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/goat.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/gptfuzzer.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/image.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/multimodal.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/pair.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/prompt.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/rainbow.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/renellm.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/reporting/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/reporting/json_report.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/reporting/llm_summary.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/reporting/markdown.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/airt/tap.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/api/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/api/client.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/api/models.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/airt.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/args.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/capability.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/dataset.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/main.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/model.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/optimize.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/runtime.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/sandbox.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/task.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/challenge/Dockerfile +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/docker-compose.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/provision.sh +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/solution.sh +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/task-remote.yaml.tmpl +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/task.yaml.tmpl +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/teardown.sh +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/templates/init/verify.sh +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/train.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/cli/worlds.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/config.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/main.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/model_catalog.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/print_mode.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/server/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/server/app.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/server/auth.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/server/prompt.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/server/utils.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/client.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/commands.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/connection.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/dreadnode.tcss +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/model_variants.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/runtime_cache.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/base.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/capabilities.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/capability_docs.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/connection_error.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/console.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/environments.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/evaluations.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/mcp.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/model_picker.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/models.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/raw_spans.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/sandboxes.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/secrets.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/sessions.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/theme_showcase.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/traces.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/screens/workspaces.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/spans_reader.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/theme.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/turn_lifecycle.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/turn_reducer.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/update_check.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/agent_dialog.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/agent_suggester.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/composer.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/context_bar.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/conversation.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/flash.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/header_bar.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/help_panel.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/mention_overlay.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/message_queue.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/overlay_mixin.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/permission_prompt.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/profile_dialog.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/prompt_info.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/session_sidebar.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/skills_dialog.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/slash_overlay.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/status_bar.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/throbber.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/tool.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/tool_progress.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/tools_dialog.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/welcome.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/app/tui/widgets/whoami.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/builtin_capabilities/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/builtin_capabilities/dreadnode/agents/dreadnode.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/builtin_capabilities/dreadnode/capability.yaml +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-runtime-reference/SKILL.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/builtin_capabilities/dreadnode/system-prompt.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/capabilities/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/capabilities/capability.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/capabilities/loader.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/capabilities/sync.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/capabilities/tool_rules.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/capabilities/types.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/conditions.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/discovery.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/environment.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/exceptions.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/execution.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/hook.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/judge.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/load.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/log.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/meta/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/meta/config.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/meta/context.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/meta/hydrate.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/meta/introspect.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/metric.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/object.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/scorer.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/serialization.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/stopping.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/task.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/transforms.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/audio.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/base.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/common.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/image.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/object_3d.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/table.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/text.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/types/video.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/core/util.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/datasets/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/datasets/dataset.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/datasets/hf.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/datasets/local.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/evaluations/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/evaluations/console.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/evaluations/evaluation.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/evaluations/events.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/evaluations/format.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/evaluations/result.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/evaluations/sample.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/caching.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/chat.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/data.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/exceptions.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/generator/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/generator/base.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/generator/http.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/generator/litellm_.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/generator/transformers_.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/generator/vllm_.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/message.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/models.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/parsing.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/tokenizer/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/tokenizer/base.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/tokenizer/transformers_.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/generators/utils.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/models/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/models/hf.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/models/local.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/models/model.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/adapters/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/adapters/agent.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/adapters/stack.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/api.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/backends/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/backends/base.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/backends/gepa.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/collectors.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/config.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/console.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/events.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/format.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/jobs.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/result.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/sampler.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/sampling.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/search.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/stopping.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/study.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/optimization/trial.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/packaging/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/packaging/loader.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/packaging/manifest.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/packaging/oci.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/packaging/package.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/packaging/task_validation.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/py.typed +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/boundary.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/fuzzing.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/graph.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/grid.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/image.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/mapelites.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/optuna.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/random.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/registry.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/samplers/strategy.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/advanced_jailbreak_detection.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/agent_security.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/agentic.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/agentic_workflow.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/classification.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/consistency.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/contains.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/cosine_sim.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/credentials.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/crucible.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/documentation_security.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/exfiltration_detection.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/format.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/harm.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/ide_security.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/image.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/json.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/judge.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/length.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/lexical.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/mcp_security.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/memorization.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/multi_agent_security.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/pii.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/prompt_leak.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/readability.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/reasoning_security.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/sentiment.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/scorers/similarity.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/skills/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/skills/creating-capabilities/SKILL.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/skills/creating-capabilities/capability-components.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/skills/creating-capabilities/capability-improvement.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/skills/creating-capabilities/runtime-default-capability.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/skills/dreadnode-cli/SKILL.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/skills/dreadnode-cli/references/command-groups.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/skills/dreadnode-cli/references/tui-crosswalk.md +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/storage/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/storage/providers.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/storage/session_store.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/storage/storage.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/_ripgrep.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/apply_patch.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/dreadnode_cli.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/editing.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/execute.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/fetch.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/glob.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/grep.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/interaction.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/ls.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/memory.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/read.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/report.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/task.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/think.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/todo.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/trajectory_search.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/web_search.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tools/write.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tracing/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tracing/constants.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tracing/convert.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tracing/exporter.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tracing/exporters.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tracing/span.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tracing/spans.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/tracing/trace_converter.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/base.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/dpo.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/etl/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/etl/_common.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/etl/rl.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/etl/sft.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/etl/worlds.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/events.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/grpo.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/jobs.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ppo.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/prime.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/async_trainer.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/config.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/coordinator.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/distributed.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/dpo.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/experience.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/fsdp2_learner.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/inference.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/learner.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/multi_turn.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/ppo.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/reward_model.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/rollout_env.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/rollout_worker.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/sft.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/ray/trainer.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/recipes.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rewards/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rewards/aggregator.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rewards/functions.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rewards/scorer_bridge.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rewards/shaping.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rewards/types.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rollouts/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rollouts/adapters.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rollouts/orchestrator.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rollouts/types.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/rollouts/worlds.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/serving/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/serving/vllm_client.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/sft.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/tinker/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/tinker/config.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/tinker/data.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/tinker/renderer.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/tinker/rl.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/tinker/trainer.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/tinker_sft.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/training/utils.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/__init__.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/advanced_jailbreak.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/adversarial_suffix.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/agent_skill.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/agentic_workflow.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/audio.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/browser_agent_attacks.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/cipher.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/constitutional.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/document.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/documentation_poison.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/encoding.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/exfiltration.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/flip_attack.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/guardrail_bypass.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/ide_injection.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/image.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/injection.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/json_tools.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/language.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/logic_bomb.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/mcp_attacks.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/multi_agent_attacks.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/persuasion.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/perturbation.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/pii_extraction.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/pythonic_tools.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/rag_poisoning.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/reasoning_attacks.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/refine.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/response_steering.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/stylistic.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/substitution.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/swap.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/system_prompt_extraction.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/text.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/video.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/transforms/xml_tools.py +0 -0
- {dreadnode-2.0.10 → dreadnode-2.0.11}/dreadnode/version.py +0 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import contextlib
|
|
2
3
|
import inspect
|
|
4
|
+
import random
|
|
5
|
+
import time
|
|
3
6
|
import typing as t
|
|
4
7
|
from contextlib import AsyncExitStack, asynccontextmanager, suppress
|
|
5
8
|
from copy import deepcopy
|
|
@@ -30,6 +33,7 @@ from dreadnode.agents.events import (
|
|
|
30
33
|
GenerationContent,
|
|
31
34
|
GenerationEnd,
|
|
32
35
|
GenerationError,
|
|
36
|
+
GenerationRetry,
|
|
33
37
|
GenerationStart,
|
|
34
38
|
GenerationStep,
|
|
35
39
|
ReactStep,
|
|
@@ -80,6 +84,45 @@ def _raise_exception(error: BaseException) -> t.NoReturn:
|
|
|
80
84
|
raise error
|
|
81
85
|
|
|
82
86
|
|
|
87
|
+
_TRANSIENT_LITELLM_EXCEPTION_NAMES: tuple[str, ...] = (
|
|
88
|
+
"RateLimitError",
|
|
89
|
+
"Timeout",
|
|
90
|
+
"APIConnectionError",
|
|
91
|
+
"APIConnectionTimeoutError",
|
|
92
|
+
"ServiceUnavailableError",
|
|
93
|
+
"InternalServerError",
|
|
94
|
+
"BadGatewayError",
|
|
95
|
+
"APIError",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _is_transient_api_error(error: BaseException) -> bool:
|
|
100
|
+
"""Classify an error as a transient LLM API failure worth retrying.
|
|
101
|
+
|
|
102
|
+
Matches an explicit allow-list of ``litellm.exceptions`` classes that
|
|
103
|
+
represent recoverable conditions: rate limits, timeouts, connection
|
|
104
|
+
failures, and 5xx responses. Notably **does not** match
|
|
105
|
+
``BadRequestError``, ``AuthenticationError``,
|
|
106
|
+
``ContextWindowExceededError`` (handled by overflow recovery), or other
|
|
107
|
+
deterministic client errors.
|
|
108
|
+
|
|
109
|
+
The allow-list is walked dynamically because ``litellm.exceptions`` does
|
|
110
|
+
not expose a single common ancestor for its transient exceptions.
|
|
111
|
+
Returns ``False`` if ``litellm`` is not importable.
|
|
112
|
+
"""
|
|
113
|
+
with contextlib.suppress(ImportError):
|
|
114
|
+
import litellm.exceptions as _litellm_exc
|
|
115
|
+
|
|
116
|
+
classes = tuple(
|
|
117
|
+
cls
|
|
118
|
+
for name in _TRANSIENT_LITELLM_EXCEPTION_NAMES
|
|
119
|
+
if (cls := getattr(_litellm_exc, name, None)) is not None
|
|
120
|
+
)
|
|
121
|
+
if classes and isinstance(error, classes):
|
|
122
|
+
return True
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
|
|
83
126
|
class Agent(Executor[AgentEvent, Trajectory]):
|
|
84
127
|
"""
|
|
85
128
|
Agent abstraction for applying tools, event logic, and message state to LLM generation.
|
|
@@ -128,6 +171,14 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
128
171
|
"""Extra parameters merged into GenerateParams for every generation (e.g. thinking config)."""
|
|
129
172
|
working_dir: Path | None = Config(default=None)
|
|
130
173
|
"""Working directory used for tool output offloading and other IO."""
|
|
174
|
+
backoff_max_tries: int = Config(default=8, ge=0)
|
|
175
|
+
"""Maximum retries on transient LLM API errors per step. ``0`` disables retry."""
|
|
176
|
+
backoff_max_time: float = Config(default=300.0, ge=0)
|
|
177
|
+
"""Maximum total seconds to spend retrying transient LLM API errors per step."""
|
|
178
|
+
backoff_base_factor: float = Config(default=1.0, ge=0)
|
|
179
|
+
"""Base factor for exponential backoff: wait = base_factor * 2 ** (attempt - 1)."""
|
|
180
|
+
backoff_jitter: bool = Config(default=True)
|
|
181
|
+
"""Whether to add up to ``backoff_base_factor`` seconds of random jitter to each wait."""
|
|
131
182
|
|
|
132
183
|
# Private state
|
|
133
184
|
_generator: Generator | None = PrivateAttr(None, init=False)
|
|
@@ -381,6 +432,49 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
381
432
|
|
|
382
433
|
raise winning_reaction
|
|
383
434
|
|
|
435
|
+
def _try_backoff(
|
|
436
|
+
self,
|
|
437
|
+
error: BaseException,
|
|
438
|
+
*,
|
|
439
|
+
tries: int,
|
|
440
|
+
start_time: float,
|
|
441
|
+
) -> float | None:
|
|
442
|
+
"""Compute a retry sleep for a transient LLM API error.
|
|
443
|
+
|
|
444
|
+
Returns the sleep duration in seconds if the caller should retry, or
|
|
445
|
+
``None`` if the error is not transient, tries are exhausted, or the
|
|
446
|
+
per-step time budget has been spent. Caller owns sleeping and emitting
|
|
447
|
+
the ``GenerationRetry`` event. Backoff is agent-owned at the error
|
|
448
|
+
site, mirroring ``_try_overflow_recovery`` — no step is consumed and
|
|
449
|
+
no hook indirection is involved.
|
|
450
|
+
"""
|
|
451
|
+
if self.backoff_max_tries <= 0:
|
|
452
|
+
return None
|
|
453
|
+
|
|
454
|
+
if not _is_transient_api_error(error):
|
|
455
|
+
return None
|
|
456
|
+
|
|
457
|
+
if tries >= self.backoff_max_tries:
|
|
458
|
+
logger.warning("Backoff aborted: max tries ({}) exceeded.", self.backoff_max_tries)
|
|
459
|
+
return None
|
|
460
|
+
|
|
461
|
+
remaining = self.backoff_max_time - (time.monotonic() - start_time)
|
|
462
|
+
if remaining <= 0:
|
|
463
|
+
logger.warning("Backoff aborted: max time ({:.2f}s) exceeded.", self.backoff_max_time)
|
|
464
|
+
return None
|
|
465
|
+
|
|
466
|
+
seconds = self.backoff_base_factor * (2**tries)
|
|
467
|
+
if self.backoff_jitter:
|
|
468
|
+
seconds += random.uniform(0, self.backoff_base_factor)
|
|
469
|
+
if seconds > remaining:
|
|
470
|
+
logger.warning(
|
|
471
|
+
"Backoff aborted: next sleep ({:.2f}s) would exceed remaining budget ({:.2f}s).",
|
|
472
|
+
seconds,
|
|
473
|
+
remaining,
|
|
474
|
+
)
|
|
475
|
+
return None
|
|
476
|
+
return seconds
|
|
477
|
+
|
|
384
478
|
async def _try_overflow_recovery(
|
|
385
479
|
self,
|
|
386
480
|
error: BaseException,
|
|
@@ -628,6 +722,43 @@ class Agent(Executor[AgentEvent, Trajectory]):
|
|
|
628
722
|
|
|
629
723
|
step_chat = await self._generate(messages)
|
|
630
724
|
|
|
725
|
+
# In-place transient-error backoff — rate limits and
|
|
726
|
+
# other litellm.APIError failures get retried at the
|
|
727
|
+
# error site with exponential backoff. No step budget
|
|
728
|
+
# consumed; clients observe GenerationRetry events
|
|
729
|
+
# rather than a spurious terminal GenerationError.
|
|
730
|
+
backoff_tries = 0
|
|
731
|
+
backoff_started = time.monotonic()
|
|
732
|
+
while step_chat.failed and step_chat.error:
|
|
733
|
+
wait = self._try_backoff(
|
|
734
|
+
step_chat.error,
|
|
735
|
+
tries=backoff_tries,
|
|
736
|
+
start_time=backoff_started,
|
|
737
|
+
)
|
|
738
|
+
if wait is None:
|
|
739
|
+
break
|
|
740
|
+
backoff_tries += 1
|
|
741
|
+
logger.warning(
|
|
742
|
+
"Backing off {:.2f}s (try {}/{})",
|
|
743
|
+
wait,
|
|
744
|
+
backoff_tries,
|
|
745
|
+
self.backoff_max_tries,
|
|
746
|
+
)
|
|
747
|
+
retry_event = GenerationRetry(
|
|
748
|
+
agent_id=self.agent_id,
|
|
749
|
+
agent_name=self.name,
|
|
750
|
+
step=step_count,
|
|
751
|
+
attempt=backoff_tries,
|
|
752
|
+
max_attempts=self.backoff_max_tries,
|
|
753
|
+
wait_seconds=wait,
|
|
754
|
+
error_type=type(step_chat.error).__name__,
|
|
755
|
+
error_message=str(step_chat.error),
|
|
756
|
+
)
|
|
757
|
+
async for event in self._dispatch(retry_event):
|
|
758
|
+
yield event
|
|
759
|
+
await asyncio.sleep(wait)
|
|
760
|
+
step_chat = await self._generate(messages)
|
|
761
|
+
|
|
631
762
|
# In-place overflow recovery — at most once per step,
|
|
632
763
|
# no step budget consumed, preserves step_count for
|
|
633
764
|
# user_message inclusion in trajectory.
|
|
@@ -311,6 +311,32 @@ class CompactionEvent(AgentEvent):
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
|
|
314
|
+
class GenerationRetry(AgentEvent):
|
|
315
|
+
"""Lifecycle event: the agent is about to sleep and retry a failed generation.
|
|
316
|
+
|
|
317
|
+
Emitted by the agent loop when a transient LLM API error (rate limit, etc.)
|
|
318
|
+
is recovered in place via ``Agent._try_backoff``. This is a lifecycle signal
|
|
319
|
+
only — it does not consume a step or land in the trajectory.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
step: int
|
|
323
|
+
attempt: int
|
|
324
|
+
max_attempts: int
|
|
325
|
+
wait_seconds: float
|
|
326
|
+
error_type: str
|
|
327
|
+
error_message: str
|
|
328
|
+
|
|
329
|
+
def _get_data(self) -> dict[str, t.Any]:
|
|
330
|
+
return {
|
|
331
|
+
"step": self.step,
|
|
332
|
+
"attempt": self.attempt,
|
|
333
|
+
"max_attempts": self.max_attempts,
|
|
334
|
+
"wait_seconds": self.wait_seconds,
|
|
335
|
+
"error_type": self.error_type,
|
|
336
|
+
"error_message": self.error_message,
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
|
|
314
340
|
class AgentStalled(AgentEvent):
|
|
315
341
|
"""Event: The agent is stalled and there are no tool calls, or stop condition).
|
|
316
342
|
|
|
@@ -1017,6 +1043,7 @@ EVENT_TYPES: dict[str, type[AgentEvent]] = {
|
|
|
1017
1043
|
"GenerationStep": GenerationStep,
|
|
1018
1044
|
"GenerationContent": GenerationContent,
|
|
1019
1045
|
"GenerationError": GenerationError,
|
|
1046
|
+
"GenerationRetry": GenerationRetry,
|
|
1020
1047
|
"ReactStep": ReactStep,
|
|
1021
1048
|
"UserInputRequired": UserInputRequired,
|
|
1022
1049
|
"Heartbeat": Heartbeat,
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Optional agent hooks: tool metrics and conversation summarization.
|
|
2
2
|
|
|
3
|
-
These hooks are
|
|
3
|
+
These hooks are opt-in — users register them explicitly on an ``Agent`` via
|
|
4
|
+
the ``hooks=`` constructor argument. Transient-error backoff is handled inline
|
|
5
|
+
by the agent loop (see ``Agent._try_backoff``) and is not a hook.
|
|
4
6
|
"""
|
|
5
7
|
|
|
6
|
-
import asyncio
|
|
7
8
|
import contextlib
|
|
8
|
-
import random
|
|
9
|
-
import time
|
|
10
9
|
import typing as t
|
|
11
10
|
from dataclasses import dataclass
|
|
12
11
|
|
|
13
|
-
from loguru import logger
|
|
14
|
-
|
|
15
12
|
from dreadnode.agents.events import (
|
|
16
|
-
AgentError,
|
|
17
13
|
AgentEvent,
|
|
18
14
|
AgentStep,
|
|
19
15
|
ToolEnd,
|
|
@@ -28,73 +24,6 @@ if t.TYPE_CHECKING:
|
|
|
28
24
|
from datetime import datetime
|
|
29
25
|
|
|
30
26
|
|
|
31
|
-
# =============================================================================
|
|
32
|
-
# Backoff
|
|
33
|
-
# =============================================================================
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _make_backoff_hook(
|
|
37
|
-
exception_types: tuple[type[Exception], ...],
|
|
38
|
-
*,
|
|
39
|
-
max_tries: int = 8,
|
|
40
|
-
max_time: float = 300.0,
|
|
41
|
-
base_factor: float = 1.0,
|
|
42
|
-
jitter: bool = True,
|
|
43
|
-
):
|
|
44
|
-
"""Create a backoff hook for specific exception types."""
|
|
45
|
-
tries = 0
|
|
46
|
-
start_time: float | None = None
|
|
47
|
-
last_step_seen = -1
|
|
48
|
-
|
|
49
|
-
@hook(AgentEvent)
|
|
50
|
-
async def backoff_hook(event: AgentEvent) -> Reaction | None:
|
|
51
|
-
nonlocal tries, start_time, last_step_seen
|
|
52
|
-
|
|
53
|
-
if isinstance(event, AgentStep):
|
|
54
|
-
if event.step > last_step_seen:
|
|
55
|
-
tries = 0
|
|
56
|
-
start_time = None
|
|
57
|
-
last_step_seen = event.step
|
|
58
|
-
return None
|
|
59
|
-
|
|
60
|
-
if not isinstance(event, AgentError) or not isinstance(event.error, exception_types):
|
|
61
|
-
return None
|
|
62
|
-
|
|
63
|
-
if start_time is None:
|
|
64
|
-
start_time = time.monotonic()
|
|
65
|
-
|
|
66
|
-
if tries >= max_tries:
|
|
67
|
-
logger.warning(f"Backoff aborted: max tries ({max_tries}) exceeded.")
|
|
68
|
-
return None
|
|
69
|
-
|
|
70
|
-
if (time.monotonic() - start_time) >= max_time:
|
|
71
|
-
logger.warning(f"Backoff aborted: max time ({max_time:.2f}s) exceeded.")
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
tries += 1
|
|
75
|
-
seconds = base_factor * (2 ** (tries - 1))
|
|
76
|
-
if jitter:
|
|
77
|
-
seconds += random.uniform(0, base_factor)
|
|
78
|
-
|
|
79
|
-
logger.warning(f"Backing off {seconds:.2f}s (try {tries}/{max_tries})")
|
|
80
|
-
await asyncio.sleep(seconds)
|
|
81
|
-
return Retry()
|
|
82
|
-
|
|
83
|
-
return backoff_hook
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
# Pre-instantiated for common LLM rate limit errors
|
|
87
|
-
backoff_on_ratelimit: Hook | None = None
|
|
88
|
-
try:
|
|
89
|
-
import litellm.exceptions
|
|
90
|
-
|
|
91
|
-
backoff_on_ratelimit = _make_backoff_hook(
|
|
92
|
-
(litellm.exceptions.RateLimitError, litellm.exceptions.APIError),
|
|
93
|
-
)
|
|
94
|
-
except ImportError:
|
|
95
|
-
pass
|
|
96
|
-
|
|
97
|
-
|
|
98
27
|
# =============================================================================
|
|
99
28
|
# Tool Metrics
|
|
100
29
|
# =============================================================================
|
|
@@ -1181,6 +1181,30 @@ def get_sample(
|
|
|
1181
1181
|
console.print()
|
|
1182
1182
|
console.print(f"{_label('Error')}[red]{error}[/red]")
|
|
1183
1183
|
|
|
1184
|
+
# Phase-aware provisioning failure — written by the eval worker when the
|
|
1185
|
+
# env-sandbox / runtime-sandbox phases fail outside of a task-authored
|
|
1186
|
+
# provision script. Surfaces which phase stalled + elapsed, so operators
|
|
1187
|
+
# don't have to open Logfire for every timeout (ENG-6249).
|
|
1188
|
+
provision_failure = dreadnode_meta.get("provision_failure") or {}
|
|
1189
|
+
if provision_failure:
|
|
1190
|
+
console.print()
|
|
1191
|
+
phase_raw = str(provision_failure.get("phase") or "unknown")
|
|
1192
|
+
phase_label = {
|
|
1193
|
+
"env_sandbox": "env sandbox",
|
|
1194
|
+
"provision_script": "provision script",
|
|
1195
|
+
"runtime_sandbox": "runtime sandbox",
|
|
1196
|
+
}.get(phase_raw, phase_raw)
|
|
1197
|
+
elapsed_ms = provision_failure.get("elapsed_ms") or 0
|
|
1198
|
+
elapsed_s = int(elapsed_ms) // 1000
|
|
1199
|
+
timed_out = bool(provision_failure.get("timed_out"))
|
|
1200
|
+
verb = "timed out provisioning" if timed_out else "failed to provision"
|
|
1201
|
+
console.print(f"{_label('Provisioning')}[red]{verb} {phase_label} after {elapsed_s}s[/red]")
|
|
1202
|
+
if not timed_out:
|
|
1203
|
+
cause_type = provision_failure.get("cause_type") or ""
|
|
1204
|
+
cause_message = provision_failure.get("cause_message") or ""
|
|
1205
|
+
if cause_type or cause_message:
|
|
1206
|
+
console.print(f"{_label('Cause')}[dim]{cause_type}: {cause_message}[/dim]")
|
|
1207
|
+
|
|
1184
1208
|
# Verification result
|
|
1185
1209
|
result = payload.get("result_json")
|
|
1186
1210
|
if result:
|
|
@@ -54,7 +54,7 @@ def print_link(profile_url: str, path: str, **query: str) -> None:
|
|
|
54
54
|
qs = f"?{urlencode(query)}" if query else ""
|
|
55
55
|
url = f"{root}/{path.lstrip('/')}{qs}"
|
|
56
56
|
console.print()
|
|
57
|
-
console.print(f" [dim]→[/dim] [u][link={url}]
|
|
57
|
+
console.print(f" [dim]→[/dim] View on web: [u][link={url}]{url}[/link][/u]")
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
# ---------------------------------------------------------------------------
|
|
@@ -279,7 +279,6 @@ class DreadnodeTextualApp(App[None]):
|
|
|
279
279
|
Binding("ctrl+e", "open_evaluations", "Evals", show=False),
|
|
280
280
|
Binding("f5", "open_console", "Console", show=False),
|
|
281
281
|
Binding("ctrl+a", "select_agent", "Agent", show=False),
|
|
282
|
-
Binding("f9", "run_update", "Update", show=False),
|
|
283
282
|
Binding("ctrl+k", "select_model", "Model", show=False),
|
|
284
283
|
Binding("ctrl+shift+k", "cycle_effort", "Effort", show=False),
|
|
285
284
|
Binding("ctrl+n", "new_session", "New", show=False),
|
|
@@ -1647,7 +1646,9 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1647
1646
|
|
|
1648
1647
|
resolved_url = server_url or self._server_url
|
|
1649
1648
|
update_banner = (
|
|
1650
|
-
|
|
1649
|
+
"Update available: "
|
|
1650
|
+
f"v{VERSION} \u2192 v{self.update_available} \u2014 "
|
|
1651
|
+
"press U to update now, or /update after login"
|
|
1651
1652
|
if self.update_available
|
|
1652
1653
|
else None
|
|
1653
1654
|
)
|
|
@@ -1755,7 +1756,11 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1755
1756
|
|
|
1756
1757
|
screen = self.screen
|
|
1757
1758
|
if isinstance(screen, AuthModal):
|
|
1758
|
-
banner =
|
|
1759
|
+
banner = (
|
|
1760
|
+
"Update available: "
|
|
1761
|
+
f"v{VERSION} \u2192 v{info.latest} \u2014 "
|
|
1762
|
+
"press U to update now, or /update after login"
|
|
1763
|
+
)
|
|
1759
1764
|
screen.query_one("#auth-update-banner", Static).update(f"[bold yellow]{banner}[/]")
|
|
1760
1765
|
except Exception:
|
|
1761
1766
|
logger.opt(exception=True).debug("Failed to update auth modal banner")
|
|
@@ -2880,6 +2885,24 @@ class DreadnodeTextualApp(App[None]):
|
|
|
2880
2885
|
)
|
|
2881
2886
|
return
|
|
2882
2887
|
|
|
2888
|
+
if event_type == "generation_retry":
|
|
2889
|
+
data = (normalized.raw or {}).get("data", {})
|
|
2890
|
+
attempt = data.get("attempt", 0)
|
|
2891
|
+
max_attempts = data.get("max_attempts", 0)
|
|
2892
|
+
wait_seconds = float(data.get("wait_seconds", 0.0) or 0.0)
|
|
2893
|
+
err_type = normalized.error_type or "error"
|
|
2894
|
+
err_msg = normalized.error_text or ""
|
|
2895
|
+
detail = f": {err_msg}" if err_msg else ""
|
|
2896
|
+
label = (
|
|
2897
|
+
f"{err_type} — retrying in {wait_seconds:.1f}s "
|
|
2898
|
+
f"(attempt {attempt}/{max_attempts}){detail}"
|
|
2899
|
+
)
|
|
2900
|
+
self._append_transcript(
|
|
2901
|
+
TranscriptEntry(kind="system", title="retry", body=label),
|
|
2902
|
+
session_id,
|
|
2903
|
+
)
|
|
2904
|
+
return
|
|
2905
|
+
|
|
2883
2906
|
if event_type == "agent_stalled":
|
|
2884
2907
|
logger.warning("Agent stalled: {}", normalized.error_text)
|
|
2885
2908
|
self._append_transcript(
|
|
@@ -241,6 +241,15 @@ def normalize_event(raw: dict[str, t.Any], session_id: str) -> NormalizedEvent:
|
|
|
241
241
|
prompt_text=_as_str(payload.get("compaction_status")),
|
|
242
242
|
**base,
|
|
243
243
|
)
|
|
244
|
+
elif event_type == "generationretry":
|
|
245
|
+
error_type = _as_str(payload.get("error_type"))
|
|
246
|
+
error_message = _as_str(payload.get("error_message"))
|
|
247
|
+
normalized = NormalizedEvent(
|
|
248
|
+
type="generation_retry",
|
|
249
|
+
error_type=error_type,
|
|
250
|
+
error_text=error_message,
|
|
251
|
+
**base,
|
|
252
|
+
)
|
|
244
253
|
elif event_type == "agentstalled":
|
|
245
254
|
normalized = NormalizedEvent(
|
|
246
255
|
type="agent_stalled",
|
|
@@ -68,6 +68,7 @@ class AuthModal(ModalScreen[Profile | None]):
|
|
|
68
68
|
BINDINGS: t.ClassVar[list[Binding]] = [
|
|
69
69
|
Binding("escape", "escape", "Back / Cancel", show=False, priority=True),
|
|
70
70
|
Binding("ctrl+c", "cancel", "Cancel", show=False),
|
|
71
|
+
Binding("u", "run_update", "Update", show=False),
|
|
71
72
|
Binding("up", "move_selection(-1)", "Up", show=False),
|
|
72
73
|
Binding("down", "move_selection(1)", "Down", show=False),
|
|
73
74
|
Binding("enter", "confirm", "Confirm", show=False),
|
|
@@ -165,6 +166,11 @@ class AuthModal(ModalScreen[Profile | None]):
|
|
|
165
166
|
self._cancel_auth_workers()
|
|
166
167
|
self.dismiss(None)
|
|
167
168
|
|
|
169
|
+
def action_run_update(self) -> None:
|
|
170
|
+
handler = getattr(self.app, "action_run_update", None)
|
|
171
|
+
if callable(handler):
|
|
172
|
+
handler()
|
|
173
|
+
|
|
168
174
|
def action_escape(self) -> None:
|
|
169
175
|
if self._active_view == "method":
|
|
170
176
|
self.action_cancel()
|
|
@@ -292,6 +292,12 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
292
292
|
self._current_actions: list[tuple[str, str]] = []
|
|
293
293
|
self._logs_content: str | None = None
|
|
294
294
|
|
|
295
|
+
# In-flight action tracking — blocks double-clicks and drives the
|
|
296
|
+
# "Starting..." active state in the detail view. Mirrors the
|
|
297
|
+
# pending-action pattern in capabilities.py.
|
|
298
|
+
self._pending_action_runtime_id: str | None = None
|
|
299
|
+
self._pending_action_label: str | None = None
|
|
300
|
+
|
|
295
301
|
# ── Connected runtime helpers ────────────────────────────────────────
|
|
296
302
|
|
|
297
303
|
@property
|
|
@@ -484,7 +490,7 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
484
490
|
|
|
485
491
|
def _quick_start(self) -> None:
|
|
486
492
|
"""Start directly from list — skip detail view for idle runtimes."""
|
|
487
|
-
if self._is_app_busy():
|
|
493
|
+
if self._is_app_busy() or self._is_action_pending():
|
|
488
494
|
return
|
|
489
495
|
selected = self._selected_list_runtime()
|
|
490
496
|
if not selected:
|
|
@@ -694,6 +700,10 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
694
700
|
is_connected=is_connected,
|
|
695
701
|
is_any_remote=self._is_any_remote,
|
|
696
702
|
)
|
|
703
|
+
# While an action is in flight for this runtime, replace the action
|
|
704
|
+
# list with a single disabled noop showing progress ("Starting...").
|
|
705
|
+
if runtime_id == self._pending_action_runtime_id and self._pending_action_label:
|
|
706
|
+
actions = [("noop", self._pending_action_label)]
|
|
697
707
|
self._current_actions = actions
|
|
698
708
|
|
|
699
709
|
for i, (action_id, label) in enumerate(actions):
|
|
@@ -701,7 +711,9 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
701
711
|
text.append(" ❯ ", style=f"bold {ACCENT}")
|
|
702
712
|
else:
|
|
703
713
|
text.append(" ")
|
|
704
|
-
if action_id
|
|
714
|
+
if action_id == "noop":
|
|
715
|
+
text.append(f"{label}\n", style=FG_FAINTEST)
|
|
716
|
+
elif action_id in ("reset",):
|
|
705
717
|
text.append(f"{label}\n", style=ERROR)
|
|
706
718
|
elif action_id in ("connect", "start", "resume"):
|
|
707
719
|
text.append(f"{label}\n", style=INFO)
|
|
@@ -889,7 +901,9 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
889
901
|
# ── Action execution ─────────────────────────────────────────────────
|
|
890
902
|
|
|
891
903
|
def _execute_action(self, action_id: str) -> None:
|
|
892
|
-
if
|
|
904
|
+
if action_id == "noop":
|
|
905
|
+
return
|
|
906
|
+
if self._is_app_busy() or self._is_action_pending():
|
|
893
907
|
return
|
|
894
908
|
runtime = self._selected_runtime
|
|
895
909
|
if not runtime:
|
|
@@ -922,20 +936,55 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
922
936
|
return True
|
|
923
937
|
return False
|
|
924
938
|
|
|
939
|
+
def _is_action_pending(self) -> bool:
|
|
940
|
+
"""True if a runtime action is in flight — blocks new actions."""
|
|
941
|
+
if self._pending_action_runtime_id is not None:
|
|
942
|
+
self.notify("Action already in progress", title="Runtimes", severity="warning")
|
|
943
|
+
return True
|
|
944
|
+
return False
|
|
945
|
+
|
|
946
|
+
def _show_action_notice(self, runtime_id: str, verb: str) -> None:
|
|
947
|
+
"""Mark a runtime action as in flight. Re-renders the detail view
|
|
948
|
+
so the action list shows a disabled `{verb}...` entry."""
|
|
949
|
+
self._pending_action_runtime_id = runtime_id
|
|
950
|
+
self._pending_action_label = f"{verb}..."
|
|
951
|
+
if self.is_mounted:
|
|
952
|
+
self._render_current_view()
|
|
953
|
+
|
|
954
|
+
def _clear_pending_action(self, runtime_id: str | None = None) -> None:
|
|
955
|
+
"""Clear the pending action marker. When ``runtime_id`` is provided
|
|
956
|
+
and doesn't match the tracked id, this is a no-op — avoids one
|
|
957
|
+
worker clearing another's pending state."""
|
|
958
|
+
if (
|
|
959
|
+
runtime_id is not None
|
|
960
|
+
and self._pending_action_runtime_id is not None
|
|
961
|
+
and self._pending_action_runtime_id != runtime_id
|
|
962
|
+
):
|
|
963
|
+
return
|
|
964
|
+
had_pending = self._pending_action_runtime_id is not None
|
|
965
|
+
self._pending_action_runtime_id = None
|
|
966
|
+
self._pending_action_label = None
|
|
967
|
+
if had_pending and self.is_mounted:
|
|
968
|
+
self._render_current_view()
|
|
969
|
+
|
|
925
970
|
# ── Runtime actions ──────────────────────────────────────────────────
|
|
926
971
|
|
|
927
|
-
@work(
|
|
972
|
+
@work(group="runtime-action")
|
|
928
973
|
async def _do_pause(self, runtime_id: str) -> None:
|
|
974
|
+
self._show_action_notice(runtime_id, "Pausing")
|
|
929
975
|
self.notify(f"Pausing {runtime_id[:8]}…", title="Runtimes", severity="warning")
|
|
930
976
|
try:
|
|
931
977
|
await asyncio.to_thread(self._api.pause_runtime, self._org, self._workspace, runtime_id)
|
|
932
978
|
self.notify(f"Paused {runtime_id[:8]}", title="Runtimes", severity="information")
|
|
933
979
|
except Exception as exc:
|
|
934
980
|
self.notify(f"Pause failed: {exc}", title="Runtimes", severity="error")
|
|
981
|
+
finally:
|
|
982
|
+
self._clear_pending_action(runtime_id)
|
|
935
983
|
self._load_data()
|
|
936
984
|
|
|
937
|
-
@work(
|
|
985
|
+
@work(group="runtime-action")
|
|
938
986
|
async def _do_resume(self, runtime_id: str) -> None:
|
|
987
|
+
self._show_action_notice(runtime_id, "Resuming")
|
|
939
988
|
self.notify(f"Resuming {runtime_id[:8]}…", title="Runtimes", severity="information")
|
|
940
989
|
try:
|
|
941
990
|
await asyncio.to_thread(
|
|
@@ -944,10 +993,13 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
944
993
|
self.notify(f"Resumed {runtime_id[:8]}", title="Runtimes", severity="information")
|
|
945
994
|
except Exception as exc:
|
|
946
995
|
self.notify(f"Resume failed: {exc}", title="Runtimes", severity="error")
|
|
996
|
+
finally:
|
|
997
|
+
self._clear_pending_action(runtime_id)
|
|
947
998
|
self._load_data()
|
|
948
999
|
|
|
949
|
-
@work(
|
|
1000
|
+
@work(group="runtime-action")
|
|
950
1001
|
async def _do_reset(self, runtime_id: str) -> None:
|
|
1002
|
+
self._show_action_notice(runtime_id, "Resetting")
|
|
951
1003
|
self.notify(f"Resetting {runtime_id[:8]}…", title="Runtimes", severity="warning")
|
|
952
1004
|
try:
|
|
953
1005
|
if (
|
|
@@ -964,10 +1016,13 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
964
1016
|
self.notify(f"Reset {runtime_id[:8]}", title="Runtimes", severity="information")
|
|
965
1017
|
except Exception as exc:
|
|
966
1018
|
self.notify(f"Reset failed: {exc}", title="Runtimes", severity="error")
|
|
1019
|
+
finally:
|
|
1020
|
+
self._clear_pending_action(runtime_id)
|
|
967
1021
|
self._load_data()
|
|
968
1022
|
|
|
969
|
-
@work(
|
|
1023
|
+
@work(group="runtime-action")
|
|
970
1024
|
async def _do_keepalive(self, runtime_id: str) -> None:
|
|
1025
|
+
self._show_action_notice(runtime_id, "Extending")
|
|
971
1026
|
self.notify(f"Extending {runtime_id[:8]}…", title="Runtimes", severity="information")
|
|
972
1027
|
try:
|
|
973
1028
|
await asyncio.to_thread(
|
|
@@ -984,10 +1039,22 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
984
1039
|
)
|
|
985
1040
|
except Exception as exc:
|
|
986
1041
|
self.notify(f"Keepalive failed: {exc}", title="Runtimes", severity="error")
|
|
1042
|
+
finally:
|
|
1043
|
+
self._clear_pending_action(runtime_id)
|
|
987
1044
|
self._load_data()
|
|
988
1045
|
|
|
989
1046
|
def _cache_start_result(self, runtime_id: str, result: dict[str, t.Any]) -> bool:
|
|
990
|
-
"""Cache connection info from a start_runtime response. Returns True if cached.
|
|
1047
|
+
"""Cache connection info from a start_runtime response. Returns True if cached.
|
|
1048
|
+
|
|
1049
|
+
The server only returns ``sandbox_token`` on the request that actually
|
|
1050
|
+
provisions the sandbox. Subsequent /start calls for a still-live
|
|
1051
|
+
sandbox return ``sandbox_token=None`` (the plaintext token is never
|
|
1052
|
+
persisted). We must NOT overwrite the cache in that case — doing so
|
|
1053
|
+
blows away a valid token from our original successful start and
|
|
1054
|
+
leaves us unable to reconnect. If no token is returned, leave the
|
|
1055
|
+
cache alone; ``_do_connect`` handles staleness separately by
|
|
1056
|
+
comparing ``provider_sandbox_id``.
|
|
1057
|
+
"""
|
|
991
1058
|
from dreadnode.app.tui.runtime_cache import RuntimeTokenCache
|
|
992
1059
|
|
|
993
1060
|
sandbox_token = result.get("sandbox_token")
|
|
@@ -1003,18 +1070,20 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
1003
1070
|
provider_sandbox_id or "null",
|
|
1004
1071
|
)
|
|
1005
1072
|
|
|
1006
|
-
if sandbox_url
|
|
1007
|
-
|
|
1008
|
-
runtime_id,
|
|
1009
|
-
sandbox_url=sandbox_url,
|
|
1010
|
-
token=sandbox_token or "",
|
|
1011
|
-
provider_sandbox_id=provider_sandbox_id,
|
|
1012
|
-
)
|
|
1013
|
-
return True
|
|
1014
|
-
return False
|
|
1073
|
+
if not sandbox_token or not sandbox_url or not provider_sandbox_id:
|
|
1074
|
+
return False
|
|
1015
1075
|
|
|
1016
|
-
|
|
1076
|
+
RuntimeTokenCache().put(
|
|
1077
|
+
runtime_id,
|
|
1078
|
+
sandbox_url=sandbox_url,
|
|
1079
|
+
token=sandbox_token,
|
|
1080
|
+
provider_sandbox_id=provider_sandbox_id,
|
|
1081
|
+
)
|
|
1082
|
+
return True
|
|
1083
|
+
|
|
1084
|
+
@work(group="runtime-action")
|
|
1017
1085
|
async def _do_start(self, runtime_id: str) -> None:
|
|
1086
|
+
self._show_action_notice(runtime_id, "Starting")
|
|
1018
1087
|
self.notify(f"Starting {runtime_id[:8]}…", title="Runtimes", severity="information")
|
|
1019
1088
|
try:
|
|
1020
1089
|
result = await asyncio.to_thread(
|
|
@@ -1040,6 +1109,8 @@ class RuntimeScreen(DreadnodeScreen):
|
|
|
1040
1109
|
except Exception as exc:
|
|
1041
1110
|
logger.warning("start_runtime failed for {}: {}", runtime_id[:8], exc)
|
|
1042
1111
|
self.notify(f"Start failed: {exc}", title="Runtimes", severity="error")
|
|
1112
|
+
finally:
|
|
1113
|
+
self._clear_pending_action(runtime_id)
|
|
1043
1114
|
self._load_data()
|
|
1044
1115
|
|
|
1045
1116
|
@work(exclusive=True, group="runtime-action")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|