dreadnode 2.0.11__tar.gz → 2.0.13__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.
Files changed (495) hide show
  1. {dreadnode-2.0.11 → dreadnode-2.0.13}/PKG-INFO +2 -1
  2. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/agent.py +32 -6
  3. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/hooks.py +69 -4
  4. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/api/client.py +92 -0
  5. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/api/models.py +37 -2
  6. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/airt.py +14 -2
  7. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/args.py +24 -0
  8. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/capability.py +64 -19
  9. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/main.py +11 -2
  10. dreadnode-2.0.13/dreadnode/app/cli/runtime.py +470 -0
  11. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/task.py +132 -51
  12. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/verify.sh +1 -1
  13. dreadnode-2.0.13/dreadnode/app/client/__init__.py +1 -0
  14. dreadnode-2.0.13/dreadnode/app/client/interactive.py +537 -0
  15. dreadnode-2.0.13/dreadnode/app/client/managed_client.py +419 -0
  16. dreadnode-2.0.13/dreadnode/app/client/models.py +190 -0
  17. dreadnode-2.0.13/dreadnode/app/client/runtime_client.py +748 -0
  18. dreadnode-2.0.13/dreadnode/app/client/transports.py +436 -0
  19. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/config.py +54 -0
  20. dreadnode-2.0.13/dreadnode/app/env.py +66 -0
  21. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/main.py +37 -14
  22. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/print_mode.py +30 -25
  23. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/server/app.py +1124 -1401
  24. dreadnode-2.0.13/dreadnode/app/server/auth.py +66 -0
  25. dreadnode-2.0.13/dreadnode/app/server/capability_manager.py +772 -0
  26. dreadnode-2.0.13/dreadnode/app/server/model_resolution.py +125 -0
  27. dreadnode-2.0.13/dreadnode/app/server/prompt_registry.py +153 -0
  28. dreadnode-2.0.13/dreadnode/app/server/runtime_events.py +503 -0
  29. dreadnode-2.0.13/dreadnode/app/server/session_hydrator.py +216 -0
  30. dreadnode-2.0.13/dreadnode/app/server/session_persistence.py +351 -0
  31. dreadnode-2.0.13/dreadnode/app/server/session_policy.py +426 -0
  32. dreadnode-2.0.13/dreadnode/app/server/turn_coordinator.py +183 -0
  33. dreadnode-2.0.13/dreadnode/app/server/websocket.py +731 -0
  34. dreadnode-2.0.13/dreadnode/app/server/worker_manager.py +517 -0
  35. dreadnode-2.0.13/dreadnode/app/tui/app.py +3957 -0
  36. dreadnode-2.0.13/dreadnode/app/tui/auth_flow.py +195 -0
  37. dreadnode-2.0.13/dreadnode/app/tui/capabilities_manager.py +388 -0
  38. dreadnode-2.0.13/dreadnode/app/tui/command_dispatcher.py +754 -0
  39. dreadnode-2.0.13/dreadnode/app/tui/commands.py +79 -0
  40. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/connection.py +33 -30
  41. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/dreadnode.tcss +83 -333
  42. dreadnode-2.0.13/dreadnode/app/tui/error_handler.py +175 -0
  43. dreadnode-2.0.13/dreadnode/app/tui/model_manager.py +704 -0
  44. dreadnode-2.0.13/dreadnode/app/tui/profile_manager.py +1169 -0
  45. dreadnode-2.0.13/dreadnode/app/tui/screen_router.py +282 -0
  46. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/__init__.py +2 -2
  47. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/auth.py +5 -3
  48. dreadnode-2.0.13/dreadnode/app/tui/screens/base.py +257 -0
  49. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/capabilities.py +264 -68
  50. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/runtimes.py +62 -59
  51. dreadnode-2.0.13/dreadnode/app/tui/screens/services.py +700 -0
  52. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/sessions.py +122 -56
  53. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/workspaces.py +44 -47
  54. dreadnode-2.0.13/dreadnode/app/tui/sessions_manager.py +1631 -0
  55. dreadnode-2.0.13/dreadnode/app/tui/tool_format.py +311 -0
  56. dreadnode-2.0.13/dreadnode/app/tui/turn_coordinator.py +642 -0
  57. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/turn_lifecycle.py +25 -1
  58. dreadnode-2.0.13/dreadnode/app/tui/turn_reducer.py +348 -0
  59. dreadnode-2.0.13/dreadnode/app/tui/turn_state_phase.py +62 -0
  60. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/__init__.py +0 -2
  61. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/agent_suggester.py +1 -1
  62. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/composer.py +3 -1
  63. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/context_bar.py +14 -0
  64. dreadnode-2.0.13/dreadnode/app/tui/widgets/conversation.py +476 -0
  65. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/help_panel.py +2 -1
  66. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/tool.py +1 -1
  67. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/tools_dialog.py +7 -15
  68. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/welcome.py +1 -0
  69. dreadnode-2.0.13/dreadnode/app/tui/wire_events.py +464 -0
  70. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-runtime-reference/SKILL.md +3 -5
  71. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/capabilities/__init__.py +2 -0
  72. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/capabilities/capability.py +72 -1
  73. dreadnode-2.0.13/dreadnode/capabilities/flags.py +299 -0
  74. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/capabilities/loader.py +438 -11
  75. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/capabilities/sync.py +1 -1
  76. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/capabilities/types.py +32 -1
  77. dreadnode-2.0.13/dreadnode/capabilities/worker.py +268 -0
  78. dreadnode-2.0.13/dreadnode/capabilities/worker_runner.py +588 -0
  79. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/exceptions.py +4 -0
  80. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/generator/litellm_.py +66 -1
  81. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/jobs.py +1 -2
  82. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/packaging/manifest.py +3 -0
  83. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/packaging/oci.py +48 -0
  84. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/packaging/task_validation.py +97 -4
  85. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/storage/storage.py +132 -6
  86. dreadnode-2.0.13/dreadnode/tracing/exporter.py +66 -0
  87. {dreadnode-2.0.11 → dreadnode-2.0.13}/pyproject.toml +2 -1
  88. dreadnode-2.0.11/dreadnode/app/cli/runtime.py +0 -55
  89. dreadnode-2.0.11/dreadnode/app/server/auth.py +0 -53
  90. dreadnode-2.0.11/dreadnode/app/tui/app.py +0 -5159
  91. dreadnode-2.0.11/dreadnode/app/tui/client.py +0 -1010
  92. dreadnode-2.0.11/dreadnode/app/tui/commands.py +0 -505
  93. dreadnode-2.0.11/dreadnode/app/tui/event_contract.py +0 -342
  94. dreadnode-2.0.11/dreadnode/app/tui/screens/base.py +0 -108
  95. dreadnode-2.0.11/dreadnode/app/tui/screens/mcp.py +0 -493
  96. dreadnode-2.0.11/dreadnode/app/tui/turn_reducer.py +0 -294
  97. dreadnode-2.0.11/dreadnode/app/tui/widgets/conversation.py +0 -332
  98. dreadnode-2.0.11/dreadnode/skills/__init__.py +0 -27
  99. dreadnode-2.0.11/dreadnode/tracing/exporter.py +0 -23
  100. {dreadnode-2.0.11 → dreadnode-2.0.13}/.gitignore +0 -0
  101. {dreadnode-2.0.11 → dreadnode-2.0.13}/LICENSE +0 -0
  102. {dreadnode-2.0.11 → dreadnode-2.0.13}/README.md +0 -0
  103. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/__init__.py +0 -0
  104. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/__main__.py +0 -0
  105. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/__init__.py +0 -0
  106. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/events.py +0 -0
  107. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/exceptions.py +0 -0
  108. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/format.py +0 -0
  109. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/mcp/__init__.py +0 -0
  110. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/mcp/auth.py +0 -0
  111. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/mcp/client.py +0 -0
  112. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/mcp/config.py +0 -0
  113. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/mcp/server.py +0 -0
  114. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/reactions.py +0 -0
  115. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/skills.py +0 -0
  116. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/stopping.py +0 -0
  117. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/subagent.py +0 -0
  118. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/tools.py +0 -0
  119. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/agents/trajectory.py +0 -0
  120. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/__init__.py +0 -0
  121. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/analytics/__init__.py +0 -0
  122. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/analytics/aggregator.py +0 -0
  123. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/analytics/classifier.py +0 -0
  124. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/analytics/compliance.py +0 -0
  125. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/analytics/engine.py +0 -0
  126. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/analytics/recommendations.py +0 -0
  127. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/analytics/types.py +0 -0
  128. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/assessment.py +0 -0
  129. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/autodan_turbo.py +0 -0
  130. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/beast.py +0 -0
  131. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/compliance/__init__.py +0 -0
  132. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/compliance/atlas.py +0 -0
  133. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/compliance/nist.py +0 -0
  134. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/compliance/owasp.py +0 -0
  135. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/compliance/owasp_agentic.py +0 -0
  136. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/compliance/saif.py +0 -0
  137. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/constants.py +0 -0
  138. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/crescendo.py +0 -0
  139. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/__init__.py +0 -0
  140. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/assets/audio/adversarial_query.mp3 +0 -0
  141. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/assets/image/bomb.jpg +0 -0
  142. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/assets/image/meth.png +0 -0
  143. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/prompts/adversarial_benchmark_subset.csv +0 -0
  144. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/prompts/ai_safety.csv +0 -0
  145. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/data_exfiltration.yaml +0 -0
  146. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/goal_hijacking.yaml +0 -0
  147. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/memory_poisoning.yaml +0 -0
  148. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/privilege_escalation.yaml +0 -0
  149. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/rce.yaml +0 -0
  150. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/scope_creep.yaml +0 -0
  151. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/tool_chaining.yaml +0 -0
  152. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/tool_selection_safety.yaml +0 -0
  153. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/unbounded_agency.yaml +0 -0
  154. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/rubrics/web_chatbot_security.yaml +0 -0
  155. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/templates/crescendo/variant_1.yaml +0 -0
  156. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/templates/crescendo/variant_2.yaml +0 -0
  157. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/templates/crescendo/variant_3.yaml +0 -0
  158. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/templates/crescendo/variant_4.yaml +0 -0
  159. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/data/templates/crescendo/variant_5.yaml +0 -0
  160. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/deep_inception.py +0 -0
  161. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/drattack.py +0 -0
  162. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/events.py +0 -0
  163. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/goat.py +0 -0
  164. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/gptfuzzer.py +0 -0
  165. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/image.py +0 -0
  166. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/multimodal.py +0 -0
  167. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/pair.py +0 -0
  168. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/prompt.py +0 -0
  169. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/rainbow.py +0 -0
  170. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/renellm.py +0 -0
  171. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/reporting/__init__.py +0 -0
  172. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/reporting/json_report.py +0 -0
  173. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/reporting/llm_summary.py +0 -0
  174. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/reporting/markdown.py +0 -0
  175. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/airt/tap.py +0 -0
  176. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/__init__.py +0 -0
  177. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/api/__init__.py +0 -0
  178. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/__init__.py +0 -0
  179. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/dataset.py +0 -0
  180. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/evaluation.py +0 -0
  181. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/model.py +0 -0
  182. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/optimize.py +0 -0
  183. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/sandbox.py +0 -0
  184. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/shared.py +0 -0
  185. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/__init__.py +0 -0
  186. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/__init__.py +0 -0
  187. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/challenge/Dockerfile +0 -0
  188. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/docker-compose.yaml +0 -0
  189. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/provision.sh +0 -0
  190. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/solution.sh +0 -0
  191. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/task-remote.yaml.tmpl +0 -0
  192. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/task.yaml.tmpl +0 -0
  193. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/templates/init/teardown.sh +0 -0
  194. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/train.py +0 -0
  195. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/cli/worlds.py +0 -0
  196. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/model_catalog.py +0 -0
  197. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/server/__init__.py +0 -0
  198. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/server/prompt.py +0 -0
  199. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/server/utils.py +0 -0
  200. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/__init__.py +0 -0
  201. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/model_variants.py +0 -0
  202. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/runtime_cache.py +0 -0
  203. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/capability_docs.py +0 -0
  204. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/connection_error.py +0 -0
  205. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/console.py +0 -0
  206. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/environments.py +0 -0
  207. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/evaluations.py +0 -0
  208. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/model_picker.py +0 -0
  209. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/models.py +0 -0
  210. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/raw_spans.py +0 -0
  211. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/sandboxes.py +0 -0
  212. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/secrets.py +0 -0
  213. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/theme_showcase.py +0 -0
  214. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/screens/traces.py +0 -0
  215. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/spans_reader.py +0 -0
  216. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/theme.py +0 -0
  217. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/update_check.py +0 -0
  218. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/agent_dialog.py +0 -0
  219. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/flash.py +0 -0
  220. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/header_bar.py +0 -0
  221. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/mention_overlay.py +0 -0
  222. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/message_queue.py +0 -0
  223. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/overlay_mixin.py +0 -0
  224. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/permission_prompt.py +0 -0
  225. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/profile_dialog.py +0 -0
  226. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/prompt_info.py +0 -0
  227. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/session_sidebar.py +0 -0
  228. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/skills_dialog.py +0 -0
  229. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/slash_overlay.py +0 -0
  230. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/status_bar.py +0 -0
  231. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/throbber.py +0 -0
  232. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/tool_progress.py +0 -0
  233. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/app/tui/widgets/whoami.py +0 -0
  234. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/builtin_capabilities/__init__.py +0 -0
  235. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/builtin_capabilities/dreadnode/agents/dreadnode.md +0 -0
  236. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/builtin_capabilities/dreadnode/capability.yaml +0 -0
  237. {dreadnode-2.0.11 → dreadnode-2.0.13/dreadnode/builtin_capabilities}/dreadnode/skills/creating-capabilities/SKILL.md +0 -0
  238. {dreadnode-2.0.11 → dreadnode-2.0.13/dreadnode/builtin_capabilities}/dreadnode/skills/creating-capabilities/capability-components.md +0 -0
  239. {dreadnode-2.0.11 → dreadnode-2.0.13/dreadnode/builtin_capabilities}/dreadnode/skills/creating-capabilities/capability-improvement.md +0 -0
  240. {dreadnode-2.0.11 → dreadnode-2.0.13/dreadnode/builtin_capabilities}/dreadnode/skills/creating-capabilities/runtime-default-capability.md +0 -0
  241. {dreadnode-2.0.11 → dreadnode-2.0.13/dreadnode/builtin_capabilities}/dreadnode/skills/dreadnode-cli/SKILL.md +0 -0
  242. {dreadnode-2.0.11 → dreadnode-2.0.13/dreadnode/builtin_capabilities}/dreadnode/skills/dreadnode-cli/references/command-groups.md +0 -0
  243. {dreadnode-2.0.11 → dreadnode-2.0.13/dreadnode/builtin_capabilities}/dreadnode/skills/dreadnode-cli/references/tui-crosswalk.md +0 -0
  244. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/builtin_capabilities/dreadnode/system-prompt.md +0 -0
  245. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/capabilities/tool_rules.py +0 -0
  246. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/__init__.py +0 -0
  247. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/conditions.py +0 -0
  248. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/discovery.py +0 -0
  249. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/environment.py +0 -0
  250. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/execution.py +0 -0
  251. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/hook.py +0 -0
  252. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/judge.py +0 -0
  253. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/load.py +0 -0
  254. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/log.py +0 -0
  255. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/meta/__init__.py +0 -0
  256. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/meta/config.py +0 -0
  257. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/meta/context.py +0 -0
  258. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/meta/hydrate.py +0 -0
  259. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/meta/introspect.py +0 -0
  260. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/metric.py +0 -0
  261. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/object.py +0 -0
  262. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/scorer.py +0 -0
  263. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/serialization.py +0 -0
  264. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/stopping.py +0 -0
  265. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/task.py +0 -0
  266. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/transforms.py +0 -0
  267. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/__init__.py +0 -0
  268. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/audio.py +0 -0
  269. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/base.py +0 -0
  270. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/common.py +0 -0
  271. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/image.py +0 -0
  272. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/object_3d.py +0 -0
  273. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/table.py +0 -0
  274. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/text.py +0 -0
  275. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/types/video.py +0 -0
  276. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/core/util.py +0 -0
  277. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/datasets/__init__.py +0 -0
  278. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/datasets/dataset.py +0 -0
  279. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/datasets/hf.py +0 -0
  280. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/datasets/local.py +0 -0
  281. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/evaluations/__init__.py +0 -0
  282. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/evaluations/console.py +0 -0
  283. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/evaluations/evaluation.py +0 -0
  284. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/evaluations/events.py +0 -0
  285. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/evaluations/format.py +0 -0
  286. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/evaluations/result.py +0 -0
  287. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/evaluations/sample.py +0 -0
  288. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/__init__.py +0 -0
  289. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/caching.py +0 -0
  290. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/chat.py +0 -0
  291. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/data.py +0 -0
  292. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/exceptions.py +0 -0
  293. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/generator/__init__.py +0 -0
  294. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/generator/base.py +0 -0
  295. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/generator/http.py +0 -0
  296. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/generator/transformers_.py +0 -0
  297. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/generator/vllm_.py +0 -0
  298. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/message.py +0 -0
  299. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/models.py +0 -0
  300. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/parsing.py +0 -0
  301. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/tokenizer/__init__.py +0 -0
  302. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/tokenizer/base.py +0 -0
  303. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/tokenizer/transformers_.py +0 -0
  304. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/generators/utils.py +0 -0
  305. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/models/__init__.py +0 -0
  306. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/models/hf.py +0 -0
  307. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/models/local.py +0 -0
  308. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/models/model.py +0 -0
  309. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/__init__.py +0 -0
  310. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/adapters/__init__.py +0 -0
  311. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/adapters/agent.py +0 -0
  312. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/adapters/stack.py +0 -0
  313. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/api.py +0 -0
  314. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/backends/__init__.py +0 -0
  315. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/backends/base.py +0 -0
  316. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/backends/gepa.py +0 -0
  317. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/collectors.py +0 -0
  318. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/config.py +0 -0
  319. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/console.py +0 -0
  320. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/events.py +0 -0
  321. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/format.py +0 -0
  322. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/result.py +0 -0
  323. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/sampler.py +0 -0
  324. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/sampling.py +0 -0
  325. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/search.py +0 -0
  326. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/stopping.py +0 -0
  327. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/study.py +0 -0
  328. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/optimization/trial.py +0 -0
  329. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/packaging/__init__.py +0 -0
  330. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/packaging/loader.py +0 -0
  331. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/packaging/package.py +0 -0
  332. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/py.typed +0 -0
  333. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/__init__.py +0 -0
  334. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/boundary.py +0 -0
  335. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/fuzzing.py +0 -0
  336. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/graph.py +0 -0
  337. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/grid.py +0 -0
  338. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/image.py +0 -0
  339. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/mapelites.py +0 -0
  340. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/optuna.py +0 -0
  341. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/random.py +0 -0
  342. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/registry.py +0 -0
  343. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/samplers/strategy.py +0 -0
  344. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/__init__.py +0 -0
  345. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/advanced_jailbreak_detection.py +0 -0
  346. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/agent_security.py +0 -0
  347. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/agentic.py +0 -0
  348. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/agentic_workflow.py +0 -0
  349. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/classification.py +0 -0
  350. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/consistency.py +0 -0
  351. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/contains.py +0 -0
  352. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/cosine_sim.py +0 -0
  353. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/credentials.py +0 -0
  354. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/crucible.py +0 -0
  355. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/documentation_security.py +0 -0
  356. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/exfiltration_detection.py +0 -0
  357. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/format.py +0 -0
  358. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/harm.py +0 -0
  359. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/ide_security.py +0 -0
  360. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/image.py +0 -0
  361. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/json.py +0 -0
  362. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/judge.py +0 -0
  363. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/length.py +0 -0
  364. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/lexical.py +0 -0
  365. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/mcp_security.py +0 -0
  366. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/memorization.py +0 -0
  367. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/multi_agent_security.py +0 -0
  368. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/pii.py +0 -0
  369. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/prompt_leak.py +0 -0
  370. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/readability.py +0 -0
  371. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/reasoning_security.py +0 -0
  372. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/sentiment.py +0 -0
  373. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/scorers/similarity.py +0 -0
  374. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/storage/__init__.py +0 -0
  375. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/storage/providers.py +0 -0
  376. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/storage/session_store.py +0 -0
  377. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/__init__.py +0 -0
  378. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/_ripgrep.py +0 -0
  379. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/apply_patch.py +0 -0
  380. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/dreadnode_cli.py +0 -0
  381. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/editing.py +0 -0
  382. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/execute.py +0 -0
  383. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/fetch.py +0 -0
  384. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/glob.py +0 -0
  385. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/grep.py +0 -0
  386. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/interaction.py +0 -0
  387. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/ls.py +0 -0
  388. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/memory.py +0 -0
  389. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/read.py +0 -0
  390. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/report.py +0 -0
  391. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/task.py +0 -0
  392. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/think.py +0 -0
  393. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/todo.py +0 -0
  394. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/trajectory_search.py +0 -0
  395. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/web_search.py +0 -0
  396. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tools/write.py +0 -0
  397. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tracing/__init__.py +0 -0
  398. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tracing/constants.py +0 -0
  399. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tracing/convert.py +0 -0
  400. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tracing/exporters.py +0 -0
  401. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tracing/span.py +0 -0
  402. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tracing/spans.py +0 -0
  403. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/tracing/trace_converter.py +0 -0
  404. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/__init__.py +0 -0
  405. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/base.py +0 -0
  406. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/dpo.py +0 -0
  407. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/etl/__init__.py +0 -0
  408. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/etl/_common.py +0 -0
  409. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/etl/rl.py +0 -0
  410. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/etl/sft.py +0 -0
  411. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/etl/worlds.py +0 -0
  412. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/events.py +0 -0
  413. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/grpo.py +0 -0
  414. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/jobs.py +0 -0
  415. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ppo.py +0 -0
  416. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/prime.py +0 -0
  417. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/__init__.py +0 -0
  418. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/async_trainer.py +0 -0
  419. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/config.py +0 -0
  420. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/coordinator.py +0 -0
  421. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/distributed.py +0 -0
  422. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/dpo.py +0 -0
  423. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/experience.py +0 -0
  424. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/fsdp2_learner.py +0 -0
  425. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/inference.py +0 -0
  426. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/learner.py +0 -0
  427. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/multi_turn.py +0 -0
  428. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/ppo.py +0 -0
  429. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/reward_model.py +0 -0
  430. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/rollout_env.py +0 -0
  431. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/rollout_worker.py +0 -0
  432. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/sft.py +0 -0
  433. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/ray/trainer.py +0 -0
  434. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/recipes.py +0 -0
  435. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rewards/__init__.py +0 -0
  436. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rewards/aggregator.py +0 -0
  437. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rewards/functions.py +0 -0
  438. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rewards/scorer_bridge.py +0 -0
  439. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rewards/shaping.py +0 -0
  440. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rewards/types.py +0 -0
  441. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rollouts/__init__.py +0 -0
  442. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rollouts/adapters.py +0 -0
  443. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rollouts/orchestrator.py +0 -0
  444. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rollouts/types.py +0 -0
  445. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/rollouts/worlds.py +0 -0
  446. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/serving/__init__.py +0 -0
  447. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/serving/vllm_client.py +0 -0
  448. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/sft.py +0 -0
  449. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/tinker/__init__.py +0 -0
  450. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/tinker/config.py +0 -0
  451. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/tinker/data.py +0 -0
  452. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/tinker/renderer.py +0 -0
  453. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/tinker/rl.py +0 -0
  454. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/tinker/trainer.py +0 -0
  455. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/tinker_sft.py +0 -0
  456. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/training/utils.py +0 -0
  457. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/__init__.py +0 -0
  458. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/advanced_jailbreak.py +0 -0
  459. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/adversarial_suffix.py +0 -0
  460. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/agent_skill.py +0 -0
  461. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/agentic_workflow.py +0 -0
  462. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/audio.py +0 -0
  463. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/browser_agent_attacks.py +0 -0
  464. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/cipher.py +0 -0
  465. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/constitutional.py +0 -0
  466. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/document.py +0 -0
  467. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/documentation_poison.py +0 -0
  468. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/encoding.py +0 -0
  469. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/exfiltration.py +0 -0
  470. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/flip_attack.py +0 -0
  471. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/guardrail_bypass.py +0 -0
  472. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/ide_injection.py +0 -0
  473. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/image.py +0 -0
  474. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/injection.py +0 -0
  475. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/json_tools.py +0 -0
  476. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/language.py +0 -0
  477. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/logic_bomb.py +0 -0
  478. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/mcp_attacks.py +0 -0
  479. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/multi_agent_attacks.py +0 -0
  480. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/persuasion.py +0 -0
  481. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/perturbation.py +0 -0
  482. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/pii_extraction.py +0 -0
  483. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/pythonic_tools.py +0 -0
  484. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/rag_poisoning.py +0 -0
  485. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/reasoning_attacks.py +0 -0
  486. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/refine.py +0 -0
  487. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/response_steering.py +0 -0
  488. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/stylistic.py +0 -0
  489. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/substitution.py +0 -0
  490. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/swap.py +0 -0
  491. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/system_prompt_extraction.py +0 -0
  492. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/text.py +0 -0
  493. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/video.py +0 -0
  494. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/transforms/xml_tools.py +0 -0
  495. {dreadnode-2.0.11 → dreadnode-2.0.13}/dreadnode/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dreadnode
3
- Version: 2.0.11
3
+ Version: 2.0.13
4
4
  Summary: Dreadnode SDK
5
5
  Project-URL: Homepage, https://dreadnode.io
6
6
  Project-URL: Documentation, https://docs.dreadnode.io
@@ -10,6 +10,7 @@ Requires-Python: <3.14,>=3.11
10
10
  Requires-Dist: aiofiles<25.0.0,>=24.1.0
11
11
  Requires-Dist: art<7.0.0,>=6.5
12
12
  Requires-Dist: coolname<3.0.0,>=2.2.0
13
+ Requires-Dist: croniter<7.0.0,>=6.0.0
13
14
  Requires-Dist: cyclopts>=4.2.0
14
15
  Requires-Dist: datasets>=4.5.0
15
16
  Requires-Dist: fastapi>=0.115.0
@@ -487,6 +487,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
487
487
  than via hook indirection.
488
488
  """
489
489
  from dreadnode.agents.hooks import (
490
+ _get_model_context_budget,
490
491
  _is_context_length_error,
491
492
  find_summarization_boundary,
492
493
  summarize_conversation,
@@ -501,19 +502,38 @@ class Agent(Executor[AgentEvent, Trajectory]):
501
502
  work = list(messages)
502
503
  system_message: Message | None = work.pop(0) if work and work[0].role == "system" else None
503
504
 
504
- boundary = find_summarization_boundary(work, min_messages_to_keep=10)
505
+ summarizer = self._generator
506
+ if summarizer is None:
507
+ return None
508
+
509
+ # Cap summarizer input at ~60% of the model's token budget (~3 chars/token,
510
+ # conservative to prefer over-truncating on code/JSON-heavy content).
511
+ # This keeps the recovery call itself from overflowing the same provider
512
+ # context that triggered recovery.
513
+ budget_tokens = _get_model_context_budget(summarizer)
514
+ max_summarize_chars = int(budget_tokens * 0.6 * 3)
515
+
516
+ boundary = find_summarization_boundary(
517
+ work,
518
+ min_messages_to_keep=10,
519
+ max_summarize_chars=max_summarize_chars,
520
+ )
505
521
  if boundary == 0:
522
+ logger.info(
523
+ "Overflow recovery: no safe summarization window fits summarizer budget "
524
+ "(budget_tokens={}, max_chars={})",
525
+ budget_tokens,
526
+ max_summarize_chars,
527
+ )
506
528
  return None
507
529
 
508
530
  to_summarize = work[:boundary]
509
531
  to_keep = work[boundary:]
510
532
 
511
- summarizer = self._generator
512
- if summarizer is None:
513
- return None
514
-
515
533
  logger.info(
516
- f"Overflow recovery: summarizing {len(to_summarize)} messages, keeping {len(to_keep)}"
534
+ "Overflow recovery: summarizing {} messages, keeping {}",
535
+ len(to_summarize),
536
+ len(to_keep),
517
537
  )
518
538
 
519
539
  try:
@@ -781,6 +801,12 @@ class Agent(Executor[AgentEvent, Trajectory]):
781
801
  step_chat = await self._generate(messages)
782
802
 
783
803
  if step_chat.failed and step_chat.error:
804
+ from dreadnode.agents.hooks import _describe_generation_error
805
+
806
+ logger.error(
807
+ "Generation step failed: {}",
808
+ _describe_generation_error(step_chat.error),
809
+ )
784
810
  error_event = GenerationError(
785
811
  agent_id=self.agent_id,
786
812
  agent_name=self.name,
@@ -80,6 +80,9 @@ CONTEXT_LENGTH_ERROR_PATTERNS = [
80
80
  "token limit",
81
81
  "maximum context length",
82
82
  "is too long",
83
+ "chunk too big",
84
+ "prompt is too long",
85
+ "too many tokens",
83
86
  ]
84
87
 
85
88
 
@@ -193,32 +196,94 @@ def _is_context_length_error(error: BaseException) -> bool:
193
196
  return any(pattern in error_str for pattern in CONTEXT_LENGTH_ERROR_PATTERNS)
194
197
 
195
198
 
199
+ def _describe_generation_error(error: BaseException) -> dict[str, t.Any]:
200
+ """Best-effort structured dump of a generation error for diagnostic logs.
201
+
202
+ Returns a dict of litellm-known attributes when present. Never raises:
203
+ attribute access that fails is silently omitted, so callers can log the
204
+ result safely even for hostile or malformed exception objects.
205
+
206
+ Intended for one-per-failure diagnostic logging at the generation error
207
+ site. Presence flags are included for fields whose *content* is not
208
+ itself safe to log (raw bodies may be arbitrary length); they tell a
209
+ future debugger whether structured data exists to look at without
210
+ dumping it into every log line.
211
+ """
212
+ fields: dict[str, t.Any] = {"type": type(error).__name__}
213
+
214
+ with contextlib.suppress(Exception):
215
+ msg = str(error)
216
+ if msg:
217
+ fields["message"] = msg if len(msg) <= 500 else msg[:500] + "...<truncated>"
218
+
219
+ for attr in ("status_code", "llm_provider", "model", "request_id"):
220
+ with contextlib.suppress(Exception):
221
+ val = getattr(error, attr, None)
222
+ if val is not None:
223
+ fields[attr] = val
224
+
225
+ with contextlib.suppress(Exception):
226
+ fields["has_body"] = bool(getattr(error, "body", None))
227
+
228
+ with contextlib.suppress(Exception):
229
+ resp = getattr(error, "response", None)
230
+ if resp is not None:
231
+ text = getattr(resp, "text", "") or ""
232
+ fields["has_response_text"] = bool(text)
233
+
234
+ return fields
235
+
236
+
196
237
  def find_summarization_boundary(
197
238
  messages: list[Message],
198
239
  min_messages_to_keep: int = 10,
240
+ max_summarize_chars: int | None = None,
199
241
  ) -> int:
200
242
  """Find a clean message boundary for summarization.
201
243
 
202
- Walks messages from the start and finds the latest safe split point that
244
+ Walks messages from the start and enumerates every safe split point that
203
245
  leaves at least ``min_messages_to_keep`` messages in the "keep" portion.
204
246
  A safe boundary is after a simple assistant message (no tool calls) —
205
247
  this is the natural end of a complete conversational turn.
206
248
 
249
+ When ``max_summarize_chars`` is provided, returns the largest safe split
250
+ whose cumulative ``len(str(message))`` stays within the cap. This keeps
251
+ the summarizer call from overflowing the same provider context that
252
+ triggered recovery. ``str(message)`` is exactly what the summarizer
253
+ receives (see ``Agent._try_overflow_recovery``) so the cap and the actual
254
+ serialized input measure the same string — including elision of image
255
+ URLs (``ContentImageUrl.__str__``) and tool-call arguments
256
+ (``ToolCall.__str__``).
257
+
207
258
  Returns:
208
259
  Index splitting ``messages[:boundary]`` (to summarize) from
209
260
  ``messages[boundary:]`` (to keep). Returns ``0`` when no valid
210
261
  boundary exists.
211
262
  """
212
- best_boundary = 0
263
+ # Enumerate every safe boundary with its cumulative serialized char count.
264
+ # (0, 0) is always a valid "no compaction" candidate.
265
+ candidates: list[tuple[int, int]] = [(0, 0)]
266
+ running_chars = 0
213
267
  for i, message in enumerate(messages):
214
268
  if len(messages) - i <= min_messages_to_keep:
215
269
  break
270
+ running_chars += len(str(message))
216
271
  is_simple_assistant = message.role == "assistant" and not getattr(
217
272
  message, "tool_calls", None
218
273
  )
219
274
  if is_simple_assistant:
220
- best_boundary = i + 1
221
- return best_boundary
275
+ candidates.append((i + 1, running_chars))
276
+
277
+ if max_summarize_chars is None:
278
+ return candidates[-1][0]
279
+
280
+ # Cumulative sizes are monotonic in boundary order, so the largest
281
+ # boundary whose size fits the cap is the best match. Walk candidates
282
+ # from latest to earliest and return the first that fits.
283
+ for boundary, size in reversed(candidates):
284
+ if size <= max_summarize_chars:
285
+ return boundary
286
+ return 0
222
287
 
223
288
 
224
289
  def _get_model_context_budget(model_or_generator: "str | Generator | None") -> int:
@@ -36,6 +36,7 @@ from dreadnode.app.api.models import (
36
36
  UserSecretsList,
37
37
  Workspace,
38
38
  )
39
+ from dreadnode.core.exceptions import InsufficientCreditsError
39
40
  from dreadnode.version import VERSION
40
41
 
41
42
 
@@ -109,6 +110,11 @@ class ApiClient:
109
110
  else:
110
111
  return f"{response.status_code}: {detail}"
111
112
 
113
+ @staticmethod
114
+ def _is_credit_related_429(detail: str) -> bool:
115
+ lowered = detail.lower()
116
+ return "credit" in lowered
117
+
112
118
  def _request(
113
119
  self,
114
120
  method: str,
@@ -141,6 +147,11 @@ class ApiClient:
141
147
  response = self._request(method, path, params, json_data, data, headers)
142
148
  if response.status_code == 401:
143
149
  raise AuthenticationError(self._get_error_message(response))
150
+ if response.status_code == 429:
151
+ error_message = self._get_error_message(response)
152
+ detail = error_message.split(": ", 1)[1] if ": " in error_message else error_message
153
+ if self._is_credit_related_429(detail):
154
+ raise InsufficientCreditsError(detail)
144
155
  try:
145
156
  response.raise_for_status()
146
157
  except httpx.HTTPStatusError as e:
@@ -1086,6 +1097,64 @@ class ApiClient:
1086
1097
  )
1087
1098
  return t.cast("dict[str, t.Any]", response.json())
1088
1099
 
1100
+ def create_runtime(
1101
+ self,
1102
+ org: str,
1103
+ workspace: str,
1104
+ project: str | None = None,
1105
+ *,
1106
+ key: str | None = None,
1107
+ name: str | None = None,
1108
+ description: str | None = None,
1109
+ config: dict[str, t.Any] | None = None,
1110
+ ) -> dict[str, t.Any]:
1111
+ """POST /org/{org}/ws/{workspace}/runtimes - Ensure a runtime exists."""
1112
+ payload: dict[str, t.Any] = {}
1113
+ if project is not None:
1114
+ payload["project"] = project
1115
+ if key is not None:
1116
+ payload["key"] = key
1117
+ if name is not None:
1118
+ payload["name"] = name
1119
+ if description is not None:
1120
+ payload["description"] = description
1121
+ if config is not None:
1122
+ payload["config"] = config
1123
+ response = self.request(
1124
+ "POST",
1125
+ f"/org/{org}/ws/{workspace}/runtimes",
1126
+ json_data=payload,
1127
+ )
1128
+ return t.cast("dict[str, t.Any]", response.json())
1129
+
1130
+ def get_runtime_config(
1131
+ self,
1132
+ org: str,
1133
+ workspace: str,
1134
+ runtime_id: str,
1135
+ ) -> dict[str, t.Any]:
1136
+ """GET /org/{org}/ws/{workspace}/runtimes/{runtime_id}/config - Get runtime config."""
1137
+ response = self.request(
1138
+ "GET",
1139
+ f"/org/{org}/ws/{workspace}/runtimes/{runtime_id}/config",
1140
+ )
1141
+ return t.cast("dict[str, t.Any]", response.json())
1142
+
1143
+ def update_runtime_config(
1144
+ self,
1145
+ org: str,
1146
+ workspace: str,
1147
+ runtime_id: str,
1148
+ config: dict[str, t.Any],
1149
+ ) -> dict[str, t.Any]:
1150
+ """PUT /org/{org}/ws/{workspace}/runtimes/{runtime_id}/config - Replace runtime config."""
1151
+ response = self.request(
1152
+ "PUT",
1153
+ f"/org/{org}/ws/{workspace}/runtimes/{runtime_id}/config",
1154
+ json_data={"config": config},
1155
+ )
1156
+ return t.cast("dict[str, t.Any]", response.json())
1157
+
1089
1158
  def pause_runtime(self, org: str, workspace: str, runtime_id: str) -> dict[str, t.Any]:
1090
1159
  """POST /org/{org}/ws/{workspace}/runtimes/{runtime_id}/pause - Pause a runtime."""
1091
1160
  response = self.request(
@@ -1722,6 +1791,23 @@ class ApiClient:
1722
1791
  )
1723
1792
  return t.cast("dict[str, t.Any]", response.json())
1724
1793
 
1794
+ def set_runtime_capability_flags(
1795
+ self,
1796
+ org: str,
1797
+ workspace: str,
1798
+ runtime_id: str,
1799
+ binding_id: str,
1800
+ *,
1801
+ flags: dict[str, bool | None],
1802
+ ) -> dict[str, t.Any]:
1803
+ """PATCH /org/{org}/ws/{workspace}/runtimes/{runtime_id}/capabilities/{binding_id}."""
1804
+ response = self.request(
1805
+ "PATCH",
1806
+ f"/org/{org}/ws/{workspace}/runtimes/{runtime_id}/capabilities/{binding_id}",
1807
+ json_data={"flags": flags},
1808
+ )
1809
+ return t.cast("dict[str, t.Any]", response.json())
1810
+
1725
1811
  def update_runtime_capability(
1726
1812
  self,
1727
1813
  org: str,
@@ -1881,6 +1967,7 @@ class ApiClient:
1881
1967
  title: str | None = None,
1882
1968
  message_count: int = 0,
1883
1969
  project_id: str | None = None,
1970
+ runtime_id: str | None = None,
1884
1971
  ) -> dict[str, t.Any]:
1885
1972
  """POST /org/{org}/ws/{workspace}/sessions - Create or save a session."""
1886
1973
  payload: dict[str, t.Any] = {
@@ -1894,6 +1981,8 @@ class ApiClient:
1894
1981
  payload["title"] = title
1895
1982
  if project_id is not None:
1896
1983
  payload["project_id"] = project_id
1984
+ if runtime_id is not None:
1985
+ payload["runtime_id"] = runtime_id
1897
1986
  response = self.request("POST", f"/org/{org}/ws/{workspace}/sessions", json_data=payload)
1898
1987
  return t.cast("dict[str, t.Any]", response.json())
1899
1988
 
@@ -2500,6 +2589,7 @@ class ApiClient:
2500
2589
  *,
2501
2590
  name: str,
2502
2591
  project_id: str,
2592
+ runtime_id: str | None = None,
2503
2593
  description: str | None = None,
2504
2594
  session_id: str | None = None,
2505
2595
  target_model: str | None = None,
@@ -2517,6 +2607,8 @@ class ApiClient:
2517
2607
  payload["description"] = description
2518
2608
  if session_id is not None:
2519
2609
  payload["session_id"] = session_id
2610
+ if runtime_id is not None:
2611
+ payload["runtime_id"] = runtime_id
2520
2612
  if target_model is not None:
2521
2613
  payload["target_model"] = target_model
2522
2614
  if attacker_model is not None:
@@ -792,6 +792,9 @@ class SessionInfo(BaseModel):
792
792
  agent: str | None = None
793
793
  title: str | None = None
794
794
  preview: str | None = None
795
+ policy_name: str = "interactive"
796
+ policy_is_autonomous: bool = False
797
+ policy_display_label: str = ""
795
798
 
796
799
 
797
800
  class SessionMessage(BaseModel):
@@ -814,6 +817,7 @@ class SessionCreateRequest(BaseModel):
814
817
  default=None,
815
818
  validation_alias="generateParamsExtra",
816
819
  )
820
+ policy: str | dict[str, t.Any] | None = None
817
821
 
818
822
  model_config = ConfigDict(populate_by_name=True)
819
823
 
@@ -823,6 +827,13 @@ class SessionCreateRequest(BaseModel):
823
827
  return _normalize_default_agent_fields(data)
824
828
 
825
829
 
830
+ class SessionEventPublishRequest(BaseModel):
831
+ """Request body for publishing an event into a session's event bus."""
832
+
833
+ kind: str
834
+ payload: dict[str, t.Any] = Field(default_factory=dict)
835
+
836
+
826
837
  class SessionRestoreRequest(BaseModel):
827
838
  """Request body for restoring a server-side session runtime."""
828
839
 
@@ -882,9 +893,9 @@ class CapabilityAgentInfo(BaseModel):
882
893
  class ComponentStatusInfo(BaseModel):
883
894
  """Component-level load/runtime status."""
884
895
 
885
- kind: t.Literal["agent", "tool", "hook", "skill", "mcp_server", "capability", "check"]
896
+ kind: t.Literal["agent", "tool", "hook", "skill", "mcp_server", "capability", "check", "worker"]
886
897
  name: str
887
- status: t.Literal["ok", "error", "degraded"]
898
+ status: t.Literal["ok", "error", "degraded", "stopped", "gated_off", "enabled_failed"]
888
899
  error: str | None = None
889
900
  detail: str | None = None
890
901
  # MCP-specific optional fields
@@ -892,6 +903,20 @@ class ComponentStatusInfo(BaseModel):
892
903
  capability: str | None = None
893
904
  transport: str | None = None
894
905
  tool_count: int | None = None
906
+ # Gate-eligible components (MCP server / worker): flag names that would
907
+ # satisfy the when: predicate. Present when status is gated_off so the
908
+ # TUI can tell operators which flag to flip (CAP-FLAG-014).
909
+ when: list[str] | None = None
910
+
911
+
912
+ class FlagInfo(BaseModel):
913
+ """Resolved flag state exposed by the local runtime."""
914
+
915
+ name: str
916
+ description: str
917
+ default: bool
918
+ effective: bool
919
+ source: t.Literal["default", "binding", "env", "cli"]
895
920
 
896
921
 
897
922
  class CapabilityInfo(BaseModel):
@@ -911,6 +936,7 @@ class CapabilityInfo(BaseModel):
911
936
  enabled: bool = True
912
937
  binding_id: str | None = None
913
938
  update_available: str | None = None
939
+ flags: list[FlagInfo] = []
914
940
  components: list[ComponentStatusInfo] = []
915
941
  dependencies: dict[str, t.Any] | None = None
916
942
  checks: list[dict[str, t.Any]] | None = None
@@ -945,6 +971,15 @@ class ToolInfo(BaseModel):
945
971
  name: str
946
972
  description: str = ""
947
973
  capability: str = ""
974
+ parameters_schema: dict[str, t.Any] | None = None
975
+ """JSON Schema for the tool's parameters, if available.
976
+
977
+ Populated for first-class :class:`dreadnode.agents.tools.Tool`
978
+ instances and MCP tools; ``None`` for synthetic session-scoped
979
+ entries (``load_skill``, ``spawn_agent``) where no schema applies.
980
+ The TUI uses this to register per-tool display hints — without it,
981
+ unknown tools fall back to the generic first-string-arg heuristic.
982
+ """
948
983
 
949
984
 
950
985
  class ToolsResponse(BaseModel):
@@ -70,7 +70,14 @@ def _summarize_report(p: dict[str, t.Any]) -> str:
70
70
  def create(
71
71
  *,
72
72
  name: str,
73
- project_id: str,
73
+ project_id: t.Annotated[
74
+ str | None,
75
+ cyclopts.Parameter(help="Project ID. Defaults to the active project scope."),
76
+ ] = None,
77
+ runtime_id: t.Annotated[
78
+ str | None,
79
+ cyclopts.Parameter(help="Runtime ID. Required when the project has multiple runtimes."),
80
+ ] = None,
74
81
  description: t.Annotated[str | None, cyclopts.Parameter(help="Assessment description")] = None,
75
82
  session_id: t.Annotated[str | None, cyclopts.Parameter(help="Session ID to associate")] = None,
76
83
  target_config: t.Annotated[
@@ -91,11 +98,16 @@ def create(
91
98
  ) -> None:
92
99
  """Create a new AIRT assessment."""
93
100
  api, profile = platform.connect()
101
+ resolved_project_id = project_id or profile.project_id
102
+ if resolved_project_id is None:
103
+ raise ValueError("Project ID required. Pass --project-id or set an active project scope.")
104
+
94
105
  payload = api.create_airt_assessment(
95
106
  profile.org_key,
96
107
  profile.workspace_key,
97
108
  name=name,
98
- project_id=project_id,
109
+ project_id=resolved_project_id,
110
+ runtime_id=runtime_id,
99
111
  description=description,
100
112
  session_id=session_id,
101
113
  target_config=_parse_json_object_option(target_config, field_name="--target-config"),
@@ -199,6 +199,15 @@ class TuiArgs(PlatformScopeArgs):
199
199
  help="Enable specific capability (repeatable, exclusive)",
200
200
  ),
201
201
  ] = None
202
+ capability_flags: t.Annotated[
203
+ list[str] | None,
204
+ cyclopts.Parameter(
205
+ name="--capability-flag",
206
+ group=TUI_GROUP,
207
+ negative_iterable=(),
208
+ help="Override capability flag (repeatable, format: capability.flag=true|false)",
209
+ ),
210
+ ] = None
202
211
  prompt: t.Annotated[
203
212
  str | None,
204
213
  cyclopts.Parameter(
@@ -222,3 +231,18 @@ class TuiArgs(PlatformScopeArgs):
222
231
  help="Headless mode: execute --prompt, print response to stdout, exit",
223
232
  ),
224
233
  ] = False
234
+ auto: t.Annotated[
235
+ bool,
236
+ cyclopts.Parameter(
237
+ group=TUI_GROUP,
238
+ negative=(),
239
+ help="Launch in autonomous mode: the agent runs without human prompts, bounded by --max-steps",
240
+ ),
241
+ ] = False
242
+ max_steps: t.Annotated[
243
+ int | None,
244
+ cyclopts.Parameter(
245
+ group=TUI_GROUP,
246
+ help="Step budget for autonomous mode (defaults to 30)",
247
+ ),
248
+ ] = None
@@ -259,6 +259,50 @@ def _summarize_capability(p: dict[str, t.Any]) -> str:
259
259
  return f"[cyan]{name}[/cyan]@[dim]{version}[/dim] {_visibility_markup(visibility)}{suffix}"
260
260
 
261
261
 
262
+ def _download_capability_files(
263
+ api: t.Any,
264
+ org: str,
265
+ name: str,
266
+ version: str,
267
+ dest: Path,
268
+ ) -> None:
269
+ """Download capability files to *dest*, trying bundle then file-by-file."""
270
+ from loguru import logger
271
+
272
+ from dreadnode.capabilities.sync import _extract_tar_to_dir
273
+
274
+ # Fast path: single tar.gz bundle download
275
+ bundle_ok = False
276
+ try:
277
+ data = api.download_capability_bundle(org, name, version)
278
+ _extract_tar_to_dir(data, dest)
279
+ bundle_ok = True
280
+ except Exception:
281
+ logger.debug(
282
+ "Bundle download unavailable for {}/{}@{}, falling back to file-by-file",
283
+ org,
284
+ name,
285
+ version,
286
+ )
287
+
288
+ if bundle_ok:
289
+ return
290
+
291
+ # Slow path: fetch manifest and download each file individually
292
+ detail = api.get_capability(org, name, version)
293
+ file_manifest = detail.get("file_manifest", [])
294
+ if not file_manifest:
295
+ raise RuntimeError(f"Capability {org}/{name}@{version} has no downloadable files")
296
+
297
+ dest.mkdir(parents=True, exist_ok=True)
298
+ for entry in file_manifest:
299
+ file_path = entry["path"]
300
+ content = api.get_capability_file(org, name, version, file_path)
301
+ file_dest = dest / file_path
302
+ file_dest.parent.mkdir(parents=True, exist_ok=True)
303
+ file_dest.write_bytes(content if isinstance(content, bytes) else content.encode())
304
+
305
+
262
306
  # ---------------------------------------------------------------------------
263
307
  # push
264
308
  # ---------------------------------------------------------------------------
@@ -429,36 +473,37 @@ def info(
429
473
  def pull(
430
474
  ref: str,
431
475
  *,
432
- upgrade: t.Annotated[bool, cyclopts.Parameter(negative=())] = False,
476
+ output: t.Annotated[Path | None, cyclopts.Parameter(name="--output", alias="-o")] = None,
477
+ force: t.Annotated[bool, cyclopts.Parameter(negative=())] = False,
433
478
  platform: PlatformArgs = PlatformArgs(),
434
479
  ) -> None:
435
- """Download a capability to read or fork — does not activate it.
480
+ """Download a capability to a local directory.
436
481
 
437
- Pulls the capability from the registry and extracts it to the
438
- local package cache. Unlike ``install``, this does not activate
439
- the capability for use by agents.
482
+ Fetches the capability from the registry and writes it to disk.
483
+ Defaults to a folder named after the capability in the current
484
+ directory. Use ``--output`` to choose a different destination.
485
+
486
+ This does **not** install or activate the capability — use
487
+ ``install`` for that.
440
488
 
441
489
  Args:
442
490
  ref: Capability to pull (e.g. my-cap, my-cap@1.0.0, or acme/my-cap).
443
- upgrade: Re-download even if already cached locally.
491
+ output: Destination directory. Defaults to ./<capability-name>.
492
+ force: Overwrite the destination if it already exists.
444
493
  """
445
- dn = configured_dreadnode(platform)
446
- _api, profile = platform.connect()
447
- parsed = ArtifactRef.parse(ref, profile.org_key)
448
494
 
449
- version_suffix = f":{parsed.version}" if parsed.version else ""
450
- package_ref = f"capability://{parsed.qualified_name}{version_suffix}"
495
+ api, profile = platform.connect()
496
+ parsed = ensure_version(api, "capability", ArtifactRef.parse(ref, profile.org_key))
451
497
 
452
- result = dn.pull_package([package_ref], upgrade=upgrade)
498
+ dest = (output or Path.cwd() / parsed.name).resolve()
453
499
 
454
- if not result.success:
455
- raise RuntimeError("; ".join(result.errors) or "Pull failed")
500
+ if dest.exists():
501
+ if not force:
502
+ raise FileExistsError(f"{dest} already exists — use --force to overwrite")
503
+ shutil.rmtree(dest)
456
504
 
457
- display = parsed.with_version(result.version).format() if result.version else parsed.format()
458
- if result.dest:
459
- console.print(f"Pulled {display} to {result.dest}")
460
- else:
461
- console.print(f"Pulled {display}")
505
+ _download_capability_files(api, parsed.org, parsed.name, parsed.version, dest)
506
+ print_success(f"Pulled {parsed.format()} to {dest}")
462
507
 
463
508
 
464
509
  # ---------------------------------------------------------------------------
@@ -148,6 +148,12 @@ def create_app(tui_args: TuiArgs) -> "DreadnodeTextualApp":
148
148
  except Exception:
149
149
  profile = None
150
150
 
151
+ initial_policy: dict[str, t.Any] | None = None
152
+ if tui_args.auto:
153
+ initial_policy = {"name": "headless"}
154
+ if tui_args.max_steps is not None:
155
+ initial_policy["max_steps"] = tui_args.max_steps
156
+
151
157
  return DreadnodeTextualApp(
152
158
  profile=profile,
153
159
  platform_url=platform_url,
@@ -158,7 +164,9 @@ def create_app(tui_args: TuiArgs) -> "DreadnodeTextualApp":
158
164
  initial_prompt=tui_args.prompt,
159
165
  capabilities_dirs=tui_args.capabilities_dirs,
160
166
  capabilities=tui_args.capabilities,
167
+ capability_flags=tui_args.capability_flags,
161
168
  system_prompt=tui_args.system_prompt,
169
+ initial_policy=initial_policy,
162
170
  )
163
171
 
164
172
 
@@ -188,6 +196,7 @@ def _run_print(tui_args: TuiArgs) -> None:
188
196
  agent=tui_args.agent,
189
197
  capabilities_dirs=tui_args.capabilities_dirs,
190
198
  capabilities=tui_args.capabilities,
199
+ capability_flags=tui_args.capability_flags,
191
200
  system_prompt=tui_args.system_prompt,
192
201
  server_url=tui_args.runtime_server,
193
202
  platform_url=tui_args.server,
@@ -234,7 +243,7 @@ def login(
234
243
  ) -> None:
235
244
  """Authenticate with the Dreadnode platform."""
236
245
  from dreadnode.app.config import DEFAULT_PLATFORM_URL
237
- from dreadnode.app.tui.commands import _login_with_api_key
246
+ from dreadnode.app.tui.auth_flow import _login_with_api_key
238
247
 
239
248
  server_url = (server or DEFAULT_PLATFORM_URL).rstrip("/")
240
249
 
@@ -286,7 +295,7 @@ def _login_with_device_code(
286
295
  import httpx
287
296
 
288
297
  from dreadnode.app.api.client import ApiClient
289
- from dreadnode.app.tui.commands import _login_with_api_key
298
+ from dreadnode.app.tui.auth_flow import _login_with_api_key
290
299
 
291
300
  api = ApiClient(server_url)
292
301
  payload = api.create_device_code()