dreadnode 2.0.8__tar.gz → 2.0.10__tar.gz

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