dreadnode 2.0.3__tar.gz → 2.0.4__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.3 → dreadnode-2.0.4}/PKG-INFO +2 -2
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/task.py +3 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/main.py +59 -9
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/server/app.py +1 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/app.py +92 -6
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/client.py +13 -6
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/dreadnode.tcss +31 -5
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/auth.py +38 -33
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/capabilities.py +22 -2
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/theme.py +21 -20
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/turn_reducer.py +2 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/conversation.py +3 -1
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/tool_progress.py +26 -4
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/welcome.py +5 -1
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/packaging/task_validation.py +30 -27
- {dreadnode-2.0.3 → dreadnode-2.0.4}/pyproject.toml +2 -2
- {dreadnode-2.0.3 → dreadnode-2.0.4}/.gitignore +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/LICENSE +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/README.md +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/__main__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/agent.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/events.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/exceptions.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/format.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/hooks.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/mcp/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/mcp/auth.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/mcp/client.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/mcp/config.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/mcp/server.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/reactions.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/skills.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/stopping.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/subagent.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/tools.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/agents/trajectory.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/analytics/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/analytics/aggregator.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/analytics/classifier.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/analytics/compliance.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/analytics/engine.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/analytics/recommendations.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/analytics/types.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/assessment.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/autodan_turbo.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/beast.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/compliance/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/compliance/atlas.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/compliance/nist.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/compliance/owasp.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/compliance/owasp_agentic.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/compliance/saif.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/constants.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/crescendo.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/assets/audio/adversarial_query.mp3 +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/assets/image/bomb.jpg +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/assets/image/meth.png +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/prompts/adversarial_benchmark_subset.csv +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/prompts/ai_safety.csv +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/data_exfiltration.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/goal_hijacking.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/memory_poisoning.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/privilege_escalation.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/rce.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/scope_creep.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/tool_chaining.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/tool_selection_safety.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/unbounded_agency.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/rubrics/web_chatbot_security.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/templates/crescendo/variant_1.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/templates/crescendo/variant_2.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/templates/crescendo/variant_3.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/templates/crescendo/variant_4.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/data/templates/crescendo/variant_5.yaml +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/deep_inception.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/drattack.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/events.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/goat.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/gptfuzzer.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/image.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/multimodal.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/pair.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/prompt.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/rainbow.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/renellm.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/reporting/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/reporting/json_report.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/reporting/llm_summary.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/reporting/markdown.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/airt/tap.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/api/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/api/client.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/api/models.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/airt.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/capability.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/dataset.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/evaluation.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/main.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/model.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/optimize.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/runtime.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/shared.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/train.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/cli/worlds.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/print_mode.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/server/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/server/auth.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/server/default-agent/tools/coding.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/server/default-agent/tools/subagent.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/server/session.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/server/system-prompt.md +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/server/utils.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/commands.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/connection.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/event_contract.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/model_variants.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/runtime_cache.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/base.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/console.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/environments.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/evaluations.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/model_picker.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/runtimes.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/sandboxes.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/secrets.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/sessions.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/traces.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/screens/workspaces.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/update_check.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/agent_dialog.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/agent_suggester.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/composer.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/context_bar.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/flash.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/header_bar.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/help_panel.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/mcp_dialog.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/mention_overlay.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/message_queue.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/overlay_mixin.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/permission_prompt.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/prompt_info.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/session_sidebar.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/skills_dialog.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/slash_overlay.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/status_bar.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/throbber.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/tool.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/app/tui/widgets/tools_dialog.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/capabilities/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/capabilities/capability.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/capabilities/loader.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/capabilities/sync.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/capabilities/tool_rules.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/capabilities/types.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/conditions.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/discovery.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/environment.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/exceptions.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/execution.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/hook.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/judge.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/load.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/log.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/meta/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/meta/config.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/meta/context.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/meta/hydrate.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/meta/introspect.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/metric.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/object.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/scorer.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/serialization.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/stopping.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/task.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/transforms.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/audio.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/base.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/common.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/image.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/object_3d.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/table.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/text.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/types/video.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/core/util.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/datasets/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/datasets/dataset.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/datasets/hf.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/datasets/local.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/evaluations/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/evaluations/console.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/evaluations/evaluation.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/evaluations/events.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/evaluations/format.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/evaluations/result.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/evaluations/sample.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/caching.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/chat.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/data.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/exceptions.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/generator/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/generator/base.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/generator/http.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/generator/litellm_.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/generator/transformers_.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/generator/vllm_.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/message.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/models.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/parsing.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/tokenizer/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/tokenizer/base.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/tokenizer/transformers_.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/generators/utils.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/models/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/models/hf.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/models/local.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/models/model.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/adapters/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/adapters/agent.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/api.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/backends/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/backends/base.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/backends/gepa.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/collectors.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/config.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/console.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/events.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/format.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/jobs.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/result.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/sampler.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/sampling.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/search.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/stopping.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/study.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/optimization/trial.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/packaging/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/packaging/loader.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/packaging/manifest.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/packaging/oci.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/packaging/package.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/py.typed +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/boundary.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/fuzzing.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/graph.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/grid.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/image.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/mapelites.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/optuna.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/random.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/registry.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/samplers/strategy.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/advanced_jailbreak_detection.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/agent_security.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/agentic.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/agentic_workflow.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/classification.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/consistency.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/contains.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/cosine_sim.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/credentials.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/crucible.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/documentation_security.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/exfiltration_detection.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/format.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/harm.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/ide_security.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/image.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/json.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/judge.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/length.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/lexical.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/mcp_security.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/memorization.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/multi_agent_security.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/pii.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/prompt_leak.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/readability.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/reasoning_security.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/sentiment.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/scorers/similarity.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/storage/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/storage/providers.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/storage/session_store.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/storage/storage.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/_ripgrep.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/apply_patch.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/editing.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/execute.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/fetch.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/glob.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/grep.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/interaction.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/ls.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/memory.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/read.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/task.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/think.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/todo.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/web_search.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tools/write.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tracing/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tracing/constants.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tracing/convert.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tracing/exporter.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tracing/exporters.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tracing/span.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tracing/spans.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/tracing/trace_converter.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/base.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/dpo.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/etl/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/etl/_common.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/etl/rl.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/etl/sft.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/etl/worlds.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/events.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/grpo.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/jobs.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ppo.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/prime.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/async_trainer.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/config.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/coordinator.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/distributed.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/dpo.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/experience.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/fsdp2_learner.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/inference.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/learner.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/multi_turn.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/ppo.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/reward_model.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/rollout_env.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/rollout_worker.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/sft.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/ray/trainer.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/recipes.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rewards/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rewards/aggregator.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rewards/functions.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rewards/scorer_bridge.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rewards/shaping.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rewards/types.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rollouts/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rollouts/adapters.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rollouts/orchestrator.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rollouts/types.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/rollouts/worlds.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/serving/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/serving/vllm_client.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/sft.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/tinker/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/tinker/config.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/tinker/data.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/tinker/renderer.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/tinker/rl.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/tinker/trainer.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/tinker_sft.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/training/utils.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/__init__.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/advanced_jailbreak.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/adversarial_suffix.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/agent_skill.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/agentic_workflow.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/audio.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/browser_agent_attacks.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/cipher.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/constitutional.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/document.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/documentation_poison.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/encoding.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/exfiltration.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/flip_attack.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/guardrail_bypass.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/ide_injection.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/image.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/injection.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/json_tools.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/language.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/logic_bomb.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/mcp_attacks.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/multi_agent_attacks.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/persuasion.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/perturbation.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/pii_extraction.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/pythonic_tools.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/rag_poisoning.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/reasoning_attacks.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/refine.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/response_steering.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/stylistic.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/substitution.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/swap.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/system_prompt_extraction.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/text.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/video.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/transforms/xml_tools.py +0 -0
- {dreadnode-2.0.3 → dreadnode-2.0.4}/dreadnode/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dreadnode
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Dreadnode SDK
|
|
5
5
|
Project-URL: Homepage, https://dreadnode.io
|
|
6
6
|
Project-URL: Documentation, https://docs.dreadnode.io
|
|
@@ -19,7 +19,7 @@ Requires-Dist: gepa>=0.1.1
|
|
|
19
19
|
Requires-Dist: httpx<1.0.0,>=0.28.0
|
|
20
20
|
Requires-Dist: jsonpath-ng>=1.7.0
|
|
21
21
|
Requires-Dist: jsonref>=1.1.0
|
|
22
|
-
Requires-Dist: litellm
|
|
22
|
+
Requires-Dist: litellm<=1.82.2,>=1.80.11
|
|
23
23
|
Requires-Dist: logfire<=3.20.0,>=3.5.3
|
|
24
24
|
Requires-Dist: loguru>=0.7.3
|
|
25
25
|
Requires-Dist: mcp<2.0.0,>=1.25.0
|
|
@@ -158,6 +158,7 @@ def sync(
|
|
|
158
158
|
*,
|
|
159
159
|
force: t.Annotated[bool, cyclopts.Parameter(negative=())] = False,
|
|
160
160
|
public: t.Annotated[bool, cyclopts.Parameter(negative=())] = False,
|
|
161
|
+
workers: t.Annotated[int, cyclopts.Parameter(help="Parallel upload workers.")] = 8,
|
|
161
162
|
registry: RegistryConfig = RegistryConfig(),
|
|
162
163
|
) -> None:
|
|
163
164
|
"""Sync task environments from a directory to the platform."""
|
|
@@ -166,7 +167,9 @@ def sync(
|
|
|
166
167
|
directory,
|
|
167
168
|
force=force,
|
|
168
169
|
public=public,
|
|
170
|
+
max_workers=workers,
|
|
169
171
|
on_progress=_sync_progress,
|
|
172
|
+
on_status=lambda msg: print(msg),
|
|
170
173
|
)
|
|
171
174
|
|
|
172
175
|
total = len(result.uploaded) + len(result.skipped) + len(result.failed)
|
|
@@ -1819,13 +1819,37 @@ class Dreadnode:
|
|
|
1819
1819
|
result.blobs_skipped += oci_result.blobs_existed
|
|
1820
1820
|
return result
|
|
1821
1821
|
|
|
1822
|
+
def _fetch_remote_task_digests(self, org: str) -> dict[str, str]:
|
|
1823
|
+
"""Fetch all remote task OCI digests in bulk via paginated list.
|
|
1824
|
+
|
|
1825
|
+
Returns a mapping of ``{task_name: oci_layer_sha}`` for every task
|
|
1826
|
+
that has a recorded OCI digest.
|
|
1827
|
+
"""
|
|
1828
|
+
digests: dict[str, str] = {}
|
|
1829
|
+
page = 1
|
|
1830
|
+
while True:
|
|
1831
|
+
resp = self.api.list_tasks(org, page=page, limit=100)
|
|
1832
|
+
for item in resp.get("tasks", []):
|
|
1833
|
+
name = item.get("name")
|
|
1834
|
+
raw = item.get("oci_digest")
|
|
1835
|
+
if name and isinstance(raw, str) and raw:
|
|
1836
|
+
sha = raw.split(":", 1)[-1] if ":" in raw else raw
|
|
1837
|
+
digests[name] = sha
|
|
1838
|
+
total = resp.get("total", 0)
|
|
1839
|
+
if page * 100 >= total:
|
|
1840
|
+
break
|
|
1841
|
+
page += 1
|
|
1842
|
+
return digests
|
|
1843
|
+
|
|
1822
1844
|
def sync_environments(
|
|
1823
1845
|
self,
|
|
1824
1846
|
directory: str | Path,
|
|
1825
1847
|
*,
|
|
1826
1848
|
force: bool = False,
|
|
1827
1849
|
public: bool = False,
|
|
1850
|
+
max_workers: int = 8,
|
|
1828
1851
|
on_progress: t.Callable[[str, str, str | None], None] | None = None,
|
|
1852
|
+
on_status: t.Callable[[str], None] | None = None,
|
|
1829
1853
|
) -> EnvironmentSyncResult:
|
|
1830
1854
|
"""Sync task environments from a directory to the platform.
|
|
1831
1855
|
|
|
@@ -1837,11 +1861,15 @@ class Dreadnode:
|
|
|
1837
1861
|
directory: Root directory containing task subdirectories.
|
|
1838
1862
|
force: Upload even when the remote SHA matches.
|
|
1839
1863
|
public: Set ``is_public=True`` after upload.
|
|
1864
|
+
max_workers: Maximum parallel build/upload threads.
|
|
1840
1865
|
on_progress: Optional callback ``(name, status, error)`` for each task.
|
|
1841
1866
|
|
|
1842
1867
|
Returns:
|
|
1843
1868
|
:class:`EnvironmentSyncResult` with uploaded/skipped/failed details.
|
|
1844
1869
|
"""
|
|
1870
|
+
import concurrent.futures
|
|
1871
|
+
import threading
|
|
1872
|
+
|
|
1845
1873
|
from dreadnode.packaging.oci import build_environment
|
|
1846
1874
|
|
|
1847
1875
|
if not self.can_sync:
|
|
@@ -1851,13 +1879,18 @@ class Dreadnode:
|
|
|
1851
1879
|
|
|
1852
1880
|
from dreadnode.packaging.task_validation import discover_task_directories
|
|
1853
1881
|
|
|
1882
|
+
_status = on_status or (lambda _msg: None)
|
|
1883
|
+
|
|
1854
1884
|
root = Path(directory).resolve()
|
|
1855
1885
|
if not root.is_dir():
|
|
1856
1886
|
raise FileNotFoundError(f"Task directory not found: {root}")
|
|
1857
1887
|
|
|
1858
1888
|
org = self.session.org_key
|
|
1859
1889
|
|
|
1890
|
+
_status("Discovering tasks...")
|
|
1860
1891
|
task_dirs, conflicts = discover_task_directories(root)
|
|
1892
|
+
_status(f"Found {len(task_dirs)} task(s)")
|
|
1893
|
+
|
|
1861
1894
|
if conflicts and on_progress:
|
|
1862
1895
|
for parent, nested in conflicts:
|
|
1863
1896
|
on_progress(
|
|
@@ -1869,9 +1902,17 @@ class Dreadnode:
|
|
|
1869
1902
|
result = EnvironmentSyncResult(
|
|
1870
1903
|
failed=[(nested.name, f"nested inside {parent.name}") for parent, nested in conflicts],
|
|
1871
1904
|
)
|
|
1872
|
-
oci_client = self.storage.oci_client()
|
|
1873
1905
|
|
|
1874
|
-
|
|
1906
|
+
# Batch-fetch all remote digests (one paginated call vs N individual GETs)
|
|
1907
|
+
_status("Fetching remote digests...")
|
|
1908
|
+
remote_digests = self._fetch_remote_task_digests(org) if not force else {}
|
|
1909
|
+
_status(
|
|
1910
|
+
f"Fetched {len(remote_digests)} remote digest(s), syncing with {max_workers} workers..."
|
|
1911
|
+
)
|
|
1912
|
+
|
|
1913
|
+
lock = threading.Lock()
|
|
1914
|
+
|
|
1915
|
+
def _sync_one(task_dir: Path) -> None:
|
|
1875
1916
|
dir_name = task_dir.name
|
|
1876
1917
|
try:
|
|
1877
1918
|
env_name, env_version = _load_environment_metadata(task_dir)
|
|
@@ -1883,33 +1924,42 @@ class Dreadnode:
|
|
|
1883
1924
|
raise ValueError("Environment OCI image has no layers") # noqa: TRY301
|
|
1884
1925
|
local_sha = image.layers[0].digest.split(":", 1)[-1]
|
|
1885
1926
|
|
|
1886
|
-
remote_sha =
|
|
1927
|
+
remote_sha = remote_digests.get(env_name)
|
|
1887
1928
|
if not force and remote_sha and remote_sha == local_sha:
|
|
1888
|
-
|
|
1929
|
+
with lock:
|
|
1930
|
+
result.skipped.append(env_name)
|
|
1889
1931
|
if on_progress:
|
|
1890
1932
|
on_progress(env_name, "skipped", None)
|
|
1891
|
-
|
|
1933
|
+
return
|
|
1892
1934
|
|
|
1935
|
+
# Each thread gets its own OCI client (own httpx.Client)
|
|
1936
|
+
oci_client = self.storage.oci_client()
|
|
1893
1937
|
push_result = oci_client.push(resolved_name, env_version, image)
|
|
1894
1938
|
if not push_result.success:
|
|
1895
1939
|
msg = "; ".join(push_result.errors) or "OCI push failed"
|
|
1896
|
-
|
|
1940
|
+
with lock:
|
|
1941
|
+
result.failed.append((env_name, msg))
|
|
1897
1942
|
if on_progress:
|
|
1898
1943
|
on_progress(env_name, "failed", msg)
|
|
1899
|
-
|
|
1944
|
+
return
|
|
1900
1945
|
|
|
1901
1946
|
if public:
|
|
1902
1947
|
self.api.update_task_visibility(org, env_name, is_public=True)
|
|
1903
1948
|
|
|
1904
|
-
|
|
1949
|
+
with lock:
|
|
1950
|
+
result.uploaded.append(env_name)
|
|
1905
1951
|
if on_progress:
|
|
1906
1952
|
on_progress(env_name, "uploaded", None)
|
|
1907
1953
|
|
|
1908
1954
|
except Exception as exc:
|
|
1909
|
-
|
|
1955
|
+
with lock:
|
|
1956
|
+
result.failed.append((dir_name, str(exc)))
|
|
1910
1957
|
if on_progress:
|
|
1911
1958
|
on_progress(dir_name, "failed", str(exc))
|
|
1912
1959
|
|
|
1960
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as pool:
|
|
1961
|
+
list(pool.map(_sync_one, task_dirs))
|
|
1962
|
+
|
|
1913
1963
|
return result
|
|
1914
1964
|
|
|
1915
1965
|
def pull_package(
|
|
@@ -549,6 +549,11 @@ class DreadnodeTextualApp(App[None]):
|
|
|
549
549
|
@on(ComposerInput.Submitted)
|
|
550
550
|
def _on_composer_submitted(self, event: ComposerInput.Submitted) -> None:
|
|
551
551
|
value = event.value
|
|
552
|
+
logger.debug(
|
|
553
|
+
"Composer submitted | length={} | has_session={}",
|
|
554
|
+
len(value),
|
|
555
|
+
self.active_session_id is not None,
|
|
556
|
+
)
|
|
552
557
|
self.query_one("#slash-overlay", SlashOverlay).hide()
|
|
553
558
|
self.query_one("#mention-overlay", MentionOverlay).hide()
|
|
554
559
|
self._dismiss_welcome()
|
|
@@ -596,7 +601,10 @@ class DreadnodeTextualApp(App[None]):
|
|
|
596
601
|
else:
|
|
597
602
|
# Show user message + thinking indicator immediately (before @work schedules)
|
|
598
603
|
# so the first render frame already shows the conversation with user input.
|
|
604
|
+
# For new sessions (no active session yet), we still show thinking state
|
|
605
|
+
# immediately so the UI feels responsive during session creation.
|
|
599
606
|
session = self._active_session()
|
|
607
|
+
conv = self.query_one("#conversation", ConversationView)
|
|
600
608
|
if session is not None:
|
|
601
609
|
sid = session.info.session_id
|
|
602
610
|
self._turn_counts.setdefault(sid, 0)
|
|
@@ -607,13 +615,10 @@ class DreadnodeTextualApp(App[None]):
|
|
|
607
615
|
record = self.sessions.get(sid)
|
|
608
616
|
if record is not None:
|
|
609
617
|
record.transcript.append(entry)
|
|
610
|
-
conv = self.query_one("#conversation", ConversationView)
|
|
611
618
|
conv.append_entry(entry)
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
else:
|
|
616
|
-
self._send_chat(value)
|
|
619
|
+
self._set_status("Thinking", busy=True)
|
|
620
|
+
conv.query_one(ToolProgress).show_activity("thinking")
|
|
621
|
+
self._send_chat(value, _user_entry_shown=session is not None)
|
|
617
622
|
|
|
618
623
|
def _submit_human_prompt_response(self, prompt: HumanPrompt, raw_value: str) -> None:
|
|
619
624
|
"""Handle composer submission while a human prompt is active."""
|
|
@@ -1140,6 +1145,7 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1140
1145
|
self._last_quit_time = now
|
|
1141
1146
|
return
|
|
1142
1147
|
self.workers.cancel_group(self, "session")
|
|
1148
|
+
self._commit_draft_to_transcript(self.active_session_id or "")
|
|
1143
1149
|
self._set_status("Interrupted", busy=False)
|
|
1144
1150
|
self._set_composer_enabled(True)
|
|
1145
1151
|
self.query_one("#composer", ComposerInput).focus()
|
|
@@ -1190,6 +1196,7 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1190
1196
|
self._flash("Prompt cancelled", severity="warning")
|
|
1191
1197
|
return
|
|
1192
1198
|
self.workers.cancel_group(self, "session")
|
|
1199
|
+
self._commit_draft_to_transcript(self.active_session_id or "")
|
|
1193
1200
|
self._set_status("Interrupted", busy=False)
|
|
1194
1201
|
self._set_composer_enabled(True)
|
|
1195
1202
|
composer.focus()
|
|
@@ -1354,6 +1361,7 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1354
1361
|
|
|
1355
1362
|
@work(exit_on_error=False)
|
|
1356
1363
|
async def _boot(self) -> None:
|
|
1364
|
+
boot_t0 = time.monotonic()
|
|
1357
1365
|
self._clear_platform_proxy_state()
|
|
1358
1366
|
self._set_status("Starting runtime", busy=True)
|
|
1359
1367
|
override_url = self._platform_override_url()
|
|
@@ -1401,6 +1409,7 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1401
1409
|
api = ApiClient(platform_profile.url, api_key=platform_profile.api_key)
|
|
1402
1410
|
backoffs = [1.0, 2.0, 4.0]
|
|
1403
1411
|
response: httpx.Response | None = None
|
|
1412
|
+
t0 = time.monotonic()
|
|
1404
1413
|
for attempt in range(len(backoffs) + 1):
|
|
1405
1414
|
try:
|
|
1406
1415
|
response = await asyncio.to_thread(api._request, "GET", "/user")
|
|
@@ -1423,6 +1432,7 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1423
1432
|
return
|
|
1424
1433
|
|
|
1425
1434
|
if response.is_success:
|
|
1435
|
+
logger.debug("Boot timing | validate_key={:.0f}ms", (time.monotonic() - t0) * 1000)
|
|
1426
1436
|
break
|
|
1427
1437
|
|
|
1428
1438
|
# Server error (5xx) — retry with backoff
|
|
@@ -1445,8 +1455,12 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1445
1455
|
return
|
|
1446
1456
|
|
|
1447
1457
|
try:
|
|
1458
|
+
t0 = time.monotonic()
|
|
1448
1459
|
await asyncio.to_thread(api.get_organization, org)
|
|
1449
1460
|
workspaces = await asyncio.to_thread(api.list_workspaces, org)
|
|
1461
|
+
logger.debug(
|
|
1462
|
+
"Boot timing | validate_org_workspace={:.0f}ms", (time.monotonic() - t0) * 1000
|
|
1463
|
+
)
|
|
1450
1464
|
except Exception as exc:
|
|
1451
1465
|
self._set_status("Authentication required", busy=False)
|
|
1452
1466
|
logger.warning("Boot: failed to validate org/workspace: {}", exc)
|
|
@@ -1462,11 +1476,21 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1462
1476
|
try:
|
|
1463
1477
|
self._connection_manager.set_api_context(api, org, workspace)
|
|
1464
1478
|
self._connection_manager.local_client.set_platform_profile(platform_profile)
|
|
1479
|
+
t0 = time.monotonic()
|
|
1465
1480
|
await self._connection_manager.local_client.start()
|
|
1481
|
+
logger.debug("Boot timing | runtime_start={:.0f}ms", (time.monotonic() - t0) * 1000)
|
|
1482
|
+
t0 = time.monotonic()
|
|
1466
1483
|
await self._refresh_runtime()
|
|
1484
|
+
logger.debug("Boot timing | refresh_runtime={:.0f}ms", (time.monotonic() - t0) * 1000)
|
|
1485
|
+
t0 = time.monotonic()
|
|
1467
1486
|
await self._refresh_server_sessions()
|
|
1487
|
+
logger.debug("Boot timing | refresh_sessions={:.0f}ms", (time.monotonic() - t0) * 1000)
|
|
1468
1488
|
try:
|
|
1489
|
+
t0 = time.monotonic()
|
|
1469
1490
|
await self._refresh_platform_models_and_key(api)
|
|
1491
|
+
logger.debug(
|
|
1492
|
+
"Boot timing | platform_models_and_key={:.0f}ms", (time.monotonic() - t0) * 1000
|
|
1493
|
+
)
|
|
1470
1494
|
except AuthenticationError:
|
|
1471
1495
|
self._handle_authentication_error("Session expired — please sign in again")
|
|
1472
1496
|
# Resume a specific session if requested, otherwise start fresh.
|
|
@@ -1477,6 +1501,7 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1477
1501
|
self._sync_conversation()
|
|
1478
1502
|
self.authenticated = True
|
|
1479
1503
|
self._refresh_skill_names()
|
|
1504
|
+
logger.info("Boot timing | total={:.0f}ms", (time.monotonic() - boot_t0) * 1000)
|
|
1480
1505
|
self._flash("Connected to Dreadnode runtime", severity="success")
|
|
1481
1506
|
self._write_activity("Connected to Dreadnode runtime", style="success")
|
|
1482
1507
|
self._set_status("Ready", busy=False)
|
|
@@ -1853,6 +1878,7 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1853
1878
|
@work(exclusive=True, group="session")
|
|
1854
1879
|
async def _switch_session(self, session_id: str) -> None:
|
|
1855
1880
|
logger.info("Session switch | session={}", session_id[:8])
|
|
1881
|
+
self._dismiss_welcome()
|
|
1856
1882
|
if self.active_session_id is not None:
|
|
1857
1883
|
self._inline_activity_sessions.discard(self.active_session_id)
|
|
1858
1884
|
self.active_session_id = session_id
|
|
@@ -1922,9 +1948,12 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1922
1948
|
|
|
1923
1949
|
@work(exit_on_error=False, exclusive=True, group="session")
|
|
1924
1950
|
async def _send_chat(self, message: str, _user_entry_shown: bool = False) -> None:
|
|
1951
|
+
send_t0 = time.monotonic()
|
|
1925
1952
|
session = self._active_session()
|
|
1926
1953
|
if session is None:
|
|
1954
|
+
t0 = time.monotonic()
|
|
1927
1955
|
await self.__create_new_session()
|
|
1956
|
+
logger.debug("Send timing | create_session={:.0f}ms", (time.monotonic() - t0) * 1000)
|
|
1928
1957
|
session = self._active_session()
|
|
1929
1958
|
if session is None:
|
|
1930
1959
|
self._flash("Failed to create a session", severity="error")
|
|
@@ -1953,7 +1982,15 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1953
1982
|
len(message),
|
|
1954
1983
|
)
|
|
1955
1984
|
try:
|
|
1985
|
+
t0 = time.monotonic()
|
|
1956
1986
|
await self._ensure_litellm_key_fresh()
|
|
1987
|
+
logger.debug(
|
|
1988
|
+
"Send timing | ensure_litellm_key={:.0f}ms", (time.monotonic() - t0) * 1000
|
|
1989
|
+
)
|
|
1990
|
+
logger.debug(
|
|
1991
|
+
"Send timing | pre_stream_total={:.0f}ms", (time.monotonic() - send_t0) * 1000
|
|
1992
|
+
)
|
|
1993
|
+
first_event = True
|
|
1957
1994
|
async for event in self.server_client.stream_chat(
|
|
1958
1995
|
session_id=session.info.session_id,
|
|
1959
1996
|
message=message,
|
|
@@ -1961,6 +1998,12 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1961
1998
|
agent=None if session.info.agent == "default" else session.info.agent,
|
|
1962
1999
|
generate_params_extra=self.generate_params_extra or None,
|
|
1963
2000
|
):
|
|
2001
|
+
if first_event:
|
|
2002
|
+
logger.debug(
|
|
2003
|
+
"Send timing | first_sse_event={:.0f}ms",
|
|
2004
|
+
(time.monotonic() - send_t0) * 1000,
|
|
2005
|
+
)
|
|
2006
|
+
first_event = False
|
|
1964
2007
|
self._handle_event(event, session.info.session_id)
|
|
1965
2008
|
except AuthenticationError:
|
|
1966
2009
|
self._handle_authentication_error("Session expired — please sign in again")
|
|
@@ -2338,6 +2381,13 @@ class DreadnodeTextualApp(App[None]):
|
|
|
2338
2381
|
|
|
2339
2382
|
if event_type == "agent_end":
|
|
2340
2383
|
stop_reason = str(payload.get("stop_reason", "")).lower()
|
|
2384
|
+
logger.debug(
|
|
2385
|
+
"Agent end | session={} | stop_reason={} | status={} | error={}",
|
|
2386
|
+
session_id[:8],
|
|
2387
|
+
stop_reason or "finished",
|
|
2388
|
+
normalized.status or "ok",
|
|
2389
|
+
normalized.error_text or "none",
|
|
2390
|
+
)
|
|
2341
2391
|
if normalized.error_text:
|
|
2342
2392
|
logger.error(
|
|
2343
2393
|
"Agent ended with error: {} (type={}, payload={})",
|
|
@@ -2380,6 +2430,34 @@ class DreadnodeTextualApp(App[None]):
|
|
|
2380
2430
|
TranscriptEntry(kind="error", title="agent", body="run ended with an error"),
|
|
2381
2431
|
session_id,
|
|
2382
2432
|
)
|
|
2433
|
+
elif stop_reason == "max_steps_reached":
|
|
2434
|
+
logger.warning(
|
|
2435
|
+
"Agent hit max steps | session={} | payload={}",
|
|
2436
|
+
session_id[:8],
|
|
2437
|
+
payload,
|
|
2438
|
+
)
|
|
2439
|
+
self._append_transcript(
|
|
2440
|
+
TranscriptEntry(
|
|
2441
|
+
kind="error",
|
|
2442
|
+
title="agent",
|
|
2443
|
+
body="stopped — reached the maximum number of steps. Send a follow-up message to continue.",
|
|
2444
|
+
),
|
|
2445
|
+
session_id,
|
|
2446
|
+
)
|
|
2447
|
+
elif stop_reason == "stalled":
|
|
2448
|
+
logger.warning(
|
|
2449
|
+
"Agent stalled | session={} | payload={}",
|
|
2450
|
+
session_id[:8],
|
|
2451
|
+
payload,
|
|
2452
|
+
)
|
|
2453
|
+
self._append_transcript(
|
|
2454
|
+
TranscriptEntry(
|
|
2455
|
+
kind="error",
|
|
2456
|
+
title="agent",
|
|
2457
|
+
body="stopped — agent appears stalled. Send a follow-up message to continue.",
|
|
2458
|
+
),
|
|
2459
|
+
session_id,
|
|
2460
|
+
)
|
|
2383
2461
|
return
|
|
2384
2462
|
|
|
2385
2463
|
if event_type == "human_prompt":
|
|
@@ -3650,7 +3728,11 @@ class DreadnodeTextualApp(App[None]):
|
|
|
3650
3728
|
async def _refresh_platform_models_and_key(self, api: ApiClient) -> None:
|
|
3651
3729
|
self._clear_platform_proxy_state()
|
|
3652
3730
|
try:
|
|
3731
|
+
t0 = time.monotonic()
|
|
3653
3732
|
preferences = await asyncio.to_thread(api.get_user_preferences)
|
|
3733
|
+
logger.debug(
|
|
3734
|
+
"Boot timing | get_user_preferences={:.0f}ms", (time.monotonic() - t0) * 1000
|
|
3735
|
+
)
|
|
3654
3736
|
except Exception as exc:
|
|
3655
3737
|
logger.warning("Failed to fetch user preferences: {}", exc)
|
|
3656
3738
|
return
|
|
@@ -3676,7 +3758,11 @@ class DreadnodeTextualApp(App[None]):
|
|
|
3676
3758
|
logger.debug("Cannot provision LiteLLM key: no org context available")
|
|
3677
3759
|
return
|
|
3678
3760
|
try:
|
|
3761
|
+
t0 = time.monotonic()
|
|
3679
3762
|
result = await asyncio.to_thread(api.provision_inference_key, org)
|
|
3763
|
+
logger.debug(
|
|
3764
|
+
"LiteLLM key provisioned | elapsed={:.0f}ms", (time.monotonic() - t0) * 1000
|
|
3765
|
+
)
|
|
3680
3766
|
except Exception as exc:
|
|
3681
3767
|
if self._is_litellm_service_unavailable(exc):
|
|
3682
3768
|
self._flash(_PLATFORM_MODELS_UNAVAILABLE_MESSAGE, severity="info")
|
|
@@ -4,6 +4,7 @@ import asyncio
|
|
|
4
4
|
import contextlib
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
|
+
import shlex
|
|
7
8
|
import subprocess
|
|
8
9
|
import tempfile
|
|
9
10
|
import time
|
|
@@ -363,8 +364,14 @@ class RuntimeServerClient:
|
|
|
363
364
|
async def _start_in_process(self) -> None:
|
|
364
365
|
"""Initialize the FastAPI app in-process and connect via ASGI transport."""
|
|
365
366
|
logger.info("Starting in-process runtime server")
|
|
367
|
+
from dreadnode.app.server.app import _warm_litellm, initialize_app
|
|
366
368
|
from dreadnode.app.server.app import app as server_app
|
|
367
|
-
|
|
369
|
+
|
|
370
|
+
# Warm litellm in background — the lifespan does this for uvicorn,
|
|
371
|
+
# but in-process mode skips lifespan events. Fire before initialize_app
|
|
372
|
+
# so the ~2s import overlaps with app init and boot network calls.
|
|
373
|
+
loop = asyncio.get_running_loop()
|
|
374
|
+
loop.run_in_executor(None, _warm_litellm)
|
|
368
375
|
|
|
369
376
|
await asyncio.to_thread(
|
|
370
377
|
initialize_app,
|
|
@@ -825,15 +832,15 @@ class RuntimeServerClient:
|
|
|
825
832
|
str(DEFAULT_SERVER_PORT),
|
|
826
833
|
]
|
|
827
834
|
if self._platform_server:
|
|
828
|
-
command.extend(["--platform-server", self._platform_server])
|
|
835
|
+
command.extend(["--platform-server", shlex.quote(self._platform_server)])
|
|
829
836
|
if self._platform_api_key:
|
|
830
|
-
command.extend(["--api-key", self._platform_api_key])
|
|
837
|
+
command.extend(["--api-key", shlex.quote(self._platform_api_key)])
|
|
831
838
|
if self._platform_organization:
|
|
832
|
-
command.extend(["--organization", self._platform_organization])
|
|
839
|
+
command.extend(["--organization", shlex.quote(self._platform_organization)])
|
|
833
840
|
if self._platform_workspace:
|
|
834
|
-
command.extend(["--workspace", self._platform_workspace])
|
|
841
|
+
command.extend(["--workspace", shlex.quote(self._platform_workspace)])
|
|
835
842
|
if self._platform_project:
|
|
836
|
-
command.extend(["--project", self._platform_project])
|
|
843
|
+
command.extend(["--project", shlex.quote(self._platform_project)])
|
|
837
844
|
|
|
838
845
|
self._owned_log_file = tempfile.NamedTemporaryFile( # noqa: SIM115 - manually owned across subprocess lifecycle
|
|
839
846
|
mode="w+",
|
|
@@ -285,9 +285,16 @@ PermissionPrompt.-active {
|
|
|
285
285
|
padding: 1 2;
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
#auth-form-wrapper {
|
|
289
|
+
width: 1fr;
|
|
290
|
+
height: auto;
|
|
291
|
+
align: center top;
|
|
292
|
+
}
|
|
293
|
+
|
|
288
294
|
#auth-content {
|
|
289
295
|
width: 1fr;
|
|
290
|
-
|
|
296
|
+
max-width: 80;
|
|
297
|
+
height: auto;
|
|
291
298
|
}
|
|
292
299
|
|
|
293
300
|
#auth-methods,
|
|
@@ -307,13 +314,13 @@ PermissionPrompt.-active {
|
|
|
307
314
|
#auth-intro {
|
|
308
315
|
color: $fg-subtle;
|
|
309
316
|
margin-bottom: 1;
|
|
310
|
-
text-align: center;
|
|
311
317
|
}
|
|
312
318
|
|
|
313
319
|
#auth-banner {
|
|
314
320
|
color: $brand;
|
|
315
321
|
text-align: center;
|
|
316
|
-
margin-
|
|
322
|
+
margin-top: 3;
|
|
323
|
+
margin-bottom: 0;
|
|
317
324
|
}
|
|
318
325
|
|
|
319
326
|
#auth-method-prompt {
|
|
@@ -374,10 +381,29 @@ PermissionPrompt.-active {
|
|
|
374
381
|
margin-bottom: 1;
|
|
375
382
|
}
|
|
376
383
|
|
|
377
|
-
#auth-
|
|
384
|
+
#auth-key-bar {
|
|
385
|
+
height: auto;
|
|
378
386
|
width: 1fr;
|
|
379
|
-
max-width: 72;
|
|
380
387
|
margin-bottom: 1;
|
|
388
|
+
padding: 0;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
#auth-key-chevron {
|
|
392
|
+
width: 2;
|
|
393
|
+
height: 1;
|
|
394
|
+
color: $accent;
|
|
395
|
+
background: transparent;
|
|
396
|
+
padding: 0;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
#auth-api-key {
|
|
400
|
+
width: 1fr;
|
|
401
|
+
border: none;
|
|
402
|
+
background: transparent;
|
|
403
|
+
color: $fg;
|
|
404
|
+
height: auto;
|
|
405
|
+
min-height: 1;
|
|
406
|
+
padding: 0;
|
|
381
407
|
}
|
|
382
408
|
|
|
383
409
|
.-hidden {
|
|
@@ -12,7 +12,7 @@ from datetime import datetime, timezone
|
|
|
12
12
|
from rich.text import Text
|
|
13
13
|
from textual import on, work
|
|
14
14
|
from textual.binding import Binding
|
|
15
|
-
from textual.containers import Container, Vertical
|
|
15
|
+
from textual.containers import Container, Horizontal, Vertical
|
|
16
16
|
from textual.screen import ModalScreen
|
|
17
17
|
from textual.widgets import Input, OptionList, Static
|
|
18
18
|
from textual.widgets.option_list import Option
|
|
@@ -62,7 +62,7 @@ class AuthModal(ModalScreen[ServerConfig | None]):
|
|
|
62
62
|
"""Full-screen auth modal — blocks all TUI interaction until authenticated."""
|
|
63
63
|
|
|
64
64
|
BINDINGS: t.ClassVar[list[Binding]] = [
|
|
65
|
-
Binding("escape", "
|
|
65
|
+
Binding("escape", "escape", "Back / Cancel", show=False, priority=True),
|
|
66
66
|
Binding("ctrl+c", "cancel", "Cancel", show=False),
|
|
67
67
|
Binding("up", "move_selection(-1)", "Up", show=False),
|
|
68
68
|
Binding("down", "move_selection(1)", "Down", show=False),
|
|
@@ -70,7 +70,6 @@ class AuthModal(ModalScreen[ServerConfig | None]):
|
|
|
70
70
|
Binding("1", "select_method(0)", "Browser", show=False),
|
|
71
71
|
Binding("2", "select_method(1)", "API key", show=False),
|
|
72
72
|
Binding("r", "retry", "Retry", show=False),
|
|
73
|
-
Binding("b", "back", "Back", show=False),
|
|
74
73
|
]
|
|
75
74
|
|
|
76
75
|
def __init__(
|
|
@@ -110,31 +109,38 @@ class AuthModal(ModalScreen[ServerConfig | None]):
|
|
|
110
109
|
self._open_browser = webbrowser.open
|
|
111
110
|
|
|
112
111
|
def compose(self) -> ComposeResult:
|
|
113
|
-
with
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
with Vertical(id="auth-modal"):
|
|
113
|
+
yield _AuthBanner(id="auth-banner")
|
|
114
|
+
yield Static("", id="auth-reason")
|
|
115
|
+
yield Static("", id="auth-update-banner")
|
|
116
|
+
with Container(id="auth-form-wrapper"), Vertical(id="auth-content"):
|
|
118
117
|
yield Static(
|
|
119
118
|
"Dreadnode can be used with your browser or by entering an API key directly.",
|
|
120
119
|
id="auth-intro",
|
|
121
120
|
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
121
|
+
with Vertical(id="auth-methods"):
|
|
122
|
+
yield Static("Select login method:", id="auth-method-prompt")
|
|
123
|
+
yield OptionList(id="auth-method-list")
|
|
124
|
+
yield Static(
|
|
125
|
+
"Use Up/Down to move, Enter/click/1/2 to select",
|
|
126
|
+
id="auth-method-help",
|
|
127
|
+
)
|
|
128
|
+
with Vertical(id="auth-device", classes="-hidden"):
|
|
129
|
+
yield Static("Waiting for browser authorization...", id="auth-status")
|
|
130
|
+
yield Static("", id="auth-user-code")
|
|
131
|
+
yield Static("", id="auth-verification-url")
|
|
132
|
+
yield Static("", id="auth-device-help")
|
|
133
|
+
yield Static("", id="auth-error")
|
|
134
|
+
with Vertical(id="auth-key", classes="-hidden"):
|
|
135
|
+
yield Static("Paste your API key:", id="auth-key-title")
|
|
136
|
+
with Horizontal(id="auth-key-bar"):
|
|
137
|
+
yield Static(">", id="auth-key-chevron")
|
|
138
|
+
yield Input(placeholder="dn_...", id="auth-api-key")
|
|
139
|
+
yield Static(
|
|
140
|
+
"Enter to submit, Esc to go back",
|
|
141
|
+
id="auth-key-help",
|
|
142
|
+
)
|
|
143
|
+
yield Static("", id="auth-key-error")
|
|
138
144
|
|
|
139
145
|
def on_mount(self) -> None:
|
|
140
146
|
if self._reason:
|
|
@@ -150,6 +156,13 @@ class AuthModal(ModalScreen[ServerConfig | None]):
|
|
|
150
156
|
self._cancel_auth_workers()
|
|
151
157
|
self.dismiss(None)
|
|
152
158
|
|
|
159
|
+
def action_escape(self) -> None:
|
|
160
|
+
if self._active_view == "method":
|
|
161
|
+
self.action_cancel()
|
|
162
|
+
else:
|
|
163
|
+
self._cancel_auth_workers()
|
|
164
|
+
self._show_method_view()
|
|
165
|
+
|
|
153
166
|
def action_move_selection(self, direction: int) -> None:
|
|
154
167
|
if self._active_view != "method":
|
|
155
168
|
return
|
|
@@ -180,12 +193,6 @@ class AuthModal(ModalScreen[ServerConfig | None]):
|
|
|
180
193
|
return
|
|
181
194
|
self._begin_device_code_flow()
|
|
182
195
|
|
|
183
|
-
def action_back(self) -> None:
|
|
184
|
-
if self._active_view == "method":
|
|
185
|
-
return
|
|
186
|
-
self._cancel_auth_workers()
|
|
187
|
-
self._show_method_view()
|
|
188
|
-
|
|
189
196
|
@on(Input.Submitted, "#auth-api-key")
|
|
190
197
|
def _on_api_key_submitted(self) -> None:
|
|
191
198
|
self._submit_api_key_from_input()
|
|
@@ -249,9 +256,7 @@ class AuthModal(ModalScreen[ServerConfig | None]):
|
|
|
249
256
|
self.query_one("#auth-methods", Vertical).add_class("-hidden")
|
|
250
257
|
self.query_one("#auth-key", Vertical).add_class("-hidden")
|
|
251
258
|
self.query_one("#auth-device", Vertical).remove_class("-hidden")
|
|
252
|
-
self.query_one("#auth-device-help", Static).update(
|
|
253
|
-
"r to retry, b to go back, Esc to cancel"
|
|
254
|
-
)
|
|
259
|
+
self.query_one("#auth-device-help", Static).update("r to retry, Esc to go back")
|
|
255
260
|
self._set_device_error("")
|
|
256
261
|
|
|
257
262
|
def _begin_device_code_flow(self) -> None:
|