dreadnode 2.0.13__tar.gz → 2.0.14__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 (532) hide show
  1. {dreadnode-2.0.13 → dreadnode-2.0.14}/.gitignore +4 -0
  2. {dreadnode-2.0.13 → dreadnode-2.0.14}/PKG-INFO +4 -4
  3. {dreadnode-2.0.13 → dreadnode-2.0.14}/README.md +3 -3
  4. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/__init__.py +2 -0
  5. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/agent.py +57 -9
  6. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/events.py +28 -16
  7. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/hooks.py +28 -7
  8. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/mcp/client.py +101 -5
  9. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/skills.py +109 -23
  10. dreadnode-2.0.14/dreadnode/agents/tool_resolution.py +56 -0
  11. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/tools.py +100 -2
  12. dreadnode-2.0.14/dreadnode/airt/__init__.py +157 -0
  13. dreadnode-2.0.14/dreadnode/airt/adversarial_reasoning.py +352 -0
  14. dreadnode-2.0.14/dreadnode/airt/advpromptier.py +273 -0
  15. dreadnode-2.0.14/dreadnode/airt/alignment_faking.py +397 -0
  16. dreadnode-2.0.14/dreadnode/airt/analogy_escalation.py +351 -0
  17. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/analytics/classifier.py +7 -1
  18. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/analytics/recommendations.py +74 -0
  19. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/analytics/types.py +6 -0
  20. dreadnode-2.0.14/dreadnode/airt/aprt_progressive.py +377 -0
  21. dreadnode-2.0.14/dreadnode/airt/attention_shifting.py +430 -0
  22. dreadnode-2.0.14/dreadnode/airt/autoredteamer.py +378 -0
  23. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/compliance/owasp_agentic.py +103 -19
  24. dreadnode-2.0.14/dreadnode/airt/cot_jailbreak.py +385 -0
  25. dreadnode-2.0.14/dreadnode/airt/echo_chamber.py +300 -0
  26. dreadnode-2.0.14/dreadnode/airt/genetic_persona.py +417 -0
  27. dreadnode-2.0.14/dreadnode/airt/goat_v2.py +385 -0
  28. dreadnode-2.0.14/dreadnode/airt/humor_bypass.py +400 -0
  29. dreadnode-2.0.14/dreadnode/airt/j2_meta.py +350 -0
  30. dreadnode-2.0.14/dreadnode/airt/jbdistill.py +379 -0
  31. dreadnode-2.0.14/dreadnode/airt/jbfuzz.py +385 -0
  32. dreadnode-2.0.14/dreadnode/airt/lrm_autonomous.py +405 -0
  33. dreadnode-2.0.14/dreadnode/airt/mapf.py +366 -0
  34. dreadnode-2.0.14/dreadnode/airt/nexus.py +357 -0
  35. dreadnode-2.0.14/dreadnode/airt/persona_hijack.py +356 -0
  36. dreadnode-2.0.14/dreadnode/airt/quantization_safety.py +289 -0
  37. dreadnode-2.0.14/dreadnode/airt/refusal_aware.py +330 -0
  38. dreadnode-2.0.14/dreadnode/airt/reward_hacking.py +310 -0
  39. dreadnode-2.0.14/dreadnode/airt/salami_slicing.py +332 -0
  40. dreadnode-2.0.14/dreadnode/airt/self_persuasion.py +365 -0
  41. dreadnode-2.0.14/dreadnode/airt/siren.py +353 -0
  42. dreadnode-2.0.14/dreadnode/airt/templatefuzz.py +385 -0
  43. dreadnode-2.0.14/dreadnode/airt/tmap_trajectory.py +378 -0
  44. dreadnode-2.0.14/dreadnode/airt/trojail.py +286 -0
  45. dreadnode-2.0.14/dreadnode/airt/watermark_removal.py +309 -0
  46. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/api/client.py +92 -0
  47. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/api/models.py +28 -1
  48. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/airt.py +12 -0
  49. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/capability.py +10 -5
  50. dreadnode-2.0.14/dreadnode/app/cli/environment.py +450 -0
  51. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/main.py +2 -0
  52. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/provision.sh +1 -1
  53. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/solution.sh +1 -1
  54. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/task-remote.yaml.tmpl +1 -1
  55. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/task.yaml.tmpl +2 -2
  56. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/teardown.sh +1 -1
  57. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/verify.sh +1 -1
  58. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/client/interactive.py +46 -2
  59. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/client/managed_client.py +207 -6
  60. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/client/models.py +28 -2
  61. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/main.py +43 -18
  62. dreadnode-2.0.14/dreadnode/app/paths.py +60 -0
  63. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/app.py +163 -38
  64. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/capability_manager.py +145 -42
  65. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/runtime_events.py +2 -0
  66. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/websocket.py +6 -2
  67. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/worker_manager.py +149 -48
  68. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/app.py +131 -2
  69. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/capabilities_manager.py +47 -0
  70. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/command_dispatcher.py +24 -8
  71. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/dreadnode.tcss +31 -0
  72. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screen_router.py +4 -1
  73. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/capabilities.py +219 -86
  74. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/console.py +4 -2
  75. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/runtimes.py +89 -4
  76. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/services.py +283 -17
  77. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/sessions_manager.py +36 -5
  78. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/turn_reducer.py +18 -3
  79. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/__init__.py +2 -0
  80. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/conversation.py +121 -4
  81. dreadnode-2.0.14/dreadnode/app/tui/widgets/new_messages_pill.py +173 -0
  82. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/tool.py +52 -9
  83. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/wire_events.py +6 -0
  84. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/__init__.py +1 -0
  85. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/capabilities/capability.py +31 -4
  86. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/capabilities/loader.py +55 -1
  87. dreadnode-2.0.14/dreadnode/capabilities/tool_rules.py +115 -0
  88. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/capabilities/worker.py +31 -2
  89. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/capabilities/worker_runner.py +130 -32
  90. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/__init__.py +8 -0
  91. dreadnode-2.0.14/dreadnode/core/environment.py +254 -0
  92. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/meta/config.py +4 -13
  93. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/metric.py +2 -2
  94. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/scorer.py +1 -1
  95. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/task.py +1 -1
  96. dreadnode-2.0.14/dreadnode/core/templating.py +133 -0
  97. dreadnode-2.0.14/dreadnode/generators/caching.py +52 -0
  98. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/generator/base.py +9 -0
  99. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/generator/litellm_.py +15 -0
  100. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/message.py +5 -2
  101. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/packaging/oci.py +14 -0
  102. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/__init__.py +44 -0
  103. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/advanced_jailbreak_detection.py +168 -0
  104. dreadnode-2.0.14/dreadnode/scorers/attack_outcome.py +439 -0
  105. dreadnode-2.0.14/dreadnode/scorers/judge_ensemble.py +424 -0
  106. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/mcp_security.py +81 -0
  107. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/reasoning_security.py +92 -0
  108. dreadnode-2.0.14/dreadnode/scorers/structural_detection.py +385 -0
  109. dreadnode-2.0.14/dreadnode/scorers/supply_chain_detection.py +582 -0
  110. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/__init__.py +10 -0
  111. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/advanced_jailbreak.py +635 -0
  112. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/adversarial_suffix.py +128 -0
  113. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/agentic_workflow.py +201 -0
  114. dreadnode-2.0.14/dreadnode/transforms/backdoor_finetune.py +859 -0
  115. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/browser_agent_attacks.py +476 -0
  116. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/cipher.py +139 -0
  117. dreadnode-2.0.14/dreadnode/transforms/competitive_parity.py +997 -0
  118. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/encoding.py +170 -0
  119. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/image.py +4 -2
  120. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/mcp_attacks.py +619 -0
  121. dreadnode-2.0.14/dreadnode/transforms/multi_agent_attacks.py +2674 -0
  122. dreadnode-2.0.14/dreadnode/transforms/multimodal_attacks.py +1283 -0
  123. dreadnode-2.0.14/dreadnode/transforms/persuasion.py +623 -0
  124. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/rag_poisoning.py +507 -0
  125. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/reasoning_attacks.py +889 -0
  126. dreadnode-2.0.14/dreadnode/transforms/structural_exploits.py +785 -0
  127. dreadnode-2.0.14/dreadnode/transforms/supply_chain.py +524 -0
  128. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/xml_tools.py +1 -1
  129. {dreadnode-2.0.13 → dreadnode-2.0.14}/pyproject.toml +1 -1
  130. dreadnode-2.0.13/dreadnode/airt/__init__.py +0 -70
  131. dreadnode-2.0.13/dreadnode/capabilities/tool_rules.py +0 -51
  132. dreadnode-2.0.13/dreadnode/core/environment.py +0 -67
  133. dreadnode-2.0.13/dreadnode/generators/caching.py +0 -42
  134. dreadnode-2.0.13/dreadnode/transforms/multi_agent_attacks.py +0 -1161
  135. dreadnode-2.0.13/dreadnode/transforms/persuasion.py +0 -312
  136. {dreadnode-2.0.13 → dreadnode-2.0.14}/LICENSE +0 -0
  137. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/__main__.py +0 -0
  138. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/__init__.py +0 -0
  139. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/exceptions.py +0 -0
  140. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/format.py +0 -0
  141. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/mcp/__init__.py +0 -0
  142. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/mcp/auth.py +0 -0
  143. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/mcp/config.py +0 -0
  144. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/mcp/server.py +0 -0
  145. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/reactions.py +0 -0
  146. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/stopping.py +0 -0
  147. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/subagent.py +0 -0
  148. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/agents/trajectory.py +0 -0
  149. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/analytics/__init__.py +0 -0
  150. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/analytics/aggregator.py +0 -0
  151. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/analytics/compliance.py +0 -0
  152. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/analytics/engine.py +0 -0
  153. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/assessment.py +0 -0
  154. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/autodan_turbo.py +0 -0
  155. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/beast.py +0 -0
  156. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/compliance/__init__.py +0 -0
  157. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/compliance/atlas.py +0 -0
  158. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/compliance/nist.py +0 -0
  159. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/compliance/owasp.py +0 -0
  160. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/compliance/saif.py +0 -0
  161. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/constants.py +0 -0
  162. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/crescendo.py +0 -0
  163. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/__init__.py +0 -0
  164. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/assets/audio/adversarial_query.mp3 +0 -0
  165. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/assets/image/bomb.jpg +0 -0
  166. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/assets/image/meth.png +0 -0
  167. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/prompts/adversarial_benchmark_subset.csv +0 -0
  168. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/prompts/ai_safety.csv +0 -0
  169. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/data_exfiltration.yaml +0 -0
  170. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/goal_hijacking.yaml +0 -0
  171. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/memory_poisoning.yaml +0 -0
  172. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/privilege_escalation.yaml +0 -0
  173. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/rce.yaml +0 -0
  174. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/scope_creep.yaml +0 -0
  175. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/tool_chaining.yaml +0 -0
  176. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/tool_selection_safety.yaml +0 -0
  177. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/unbounded_agency.yaml +0 -0
  178. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/rubrics/web_chatbot_security.yaml +0 -0
  179. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/templates/crescendo/variant_1.yaml +0 -0
  180. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/templates/crescendo/variant_2.yaml +0 -0
  181. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/templates/crescendo/variant_3.yaml +0 -0
  182. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/templates/crescendo/variant_4.yaml +0 -0
  183. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/data/templates/crescendo/variant_5.yaml +0 -0
  184. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/deep_inception.py +0 -0
  185. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/drattack.py +0 -0
  186. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/events.py +0 -0
  187. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/goat.py +0 -0
  188. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/gptfuzzer.py +0 -0
  189. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/image.py +0 -0
  190. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/multimodal.py +0 -0
  191. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/pair.py +0 -0
  192. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/prompt.py +0 -0
  193. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/rainbow.py +0 -0
  194. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/renellm.py +0 -0
  195. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/reporting/__init__.py +0 -0
  196. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/reporting/json_report.py +0 -0
  197. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/reporting/llm_summary.py +0 -0
  198. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/reporting/markdown.py +0 -0
  199. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/airt/tap.py +0 -0
  200. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/__init__.py +0 -0
  201. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/api/__init__.py +0 -0
  202. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/__init__.py +0 -0
  203. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/args.py +0 -0
  204. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/dataset.py +0 -0
  205. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/evaluation.py +0 -0
  206. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/model.py +0 -0
  207. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/optimize.py +0 -0
  208. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/runtime.py +0 -0
  209. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/sandbox.py +0 -0
  210. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/shared.py +0 -0
  211. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/task.py +0 -0
  212. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/__init__.py +0 -0
  213. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/__init__.py +0 -0
  214. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/challenge/Dockerfile +0 -0
  215. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/templates/init/docker-compose.yaml +0 -0
  216. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/train.py +0 -0
  217. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/cli/worlds.py +0 -0
  218. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/client/__init__.py +0 -0
  219. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/client/runtime_client.py +0 -0
  220. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/client/transports.py +0 -0
  221. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/config.py +0 -0
  222. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/env.py +0 -0
  223. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/model_catalog.py +0 -0
  224. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/print_mode.py +0 -0
  225. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/__init__.py +0 -0
  226. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/auth.py +0 -0
  227. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/model_resolution.py +0 -0
  228. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/prompt.py +0 -0
  229. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/prompt_registry.py +0 -0
  230. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/session_hydrator.py +0 -0
  231. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/session_persistence.py +0 -0
  232. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/session_policy.py +0 -0
  233. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/turn_coordinator.py +0 -0
  234. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/server/utils.py +0 -0
  235. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/__init__.py +0 -0
  236. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/auth_flow.py +0 -0
  237. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/commands.py +0 -0
  238. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/connection.py +0 -0
  239. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/error_handler.py +0 -0
  240. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/model_manager.py +0 -0
  241. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/model_variants.py +0 -0
  242. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/profile_manager.py +0 -0
  243. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/runtime_cache.py +0 -0
  244. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/__init__.py +0 -0
  245. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/auth.py +0 -0
  246. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/base.py +0 -0
  247. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/capability_docs.py +0 -0
  248. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/connection_error.py +0 -0
  249. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/environments.py +0 -0
  250. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/evaluations.py +0 -0
  251. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/model_picker.py +0 -0
  252. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/models.py +0 -0
  253. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/raw_spans.py +0 -0
  254. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/sandboxes.py +0 -0
  255. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/secrets.py +0 -0
  256. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/sessions.py +0 -0
  257. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/theme_showcase.py +0 -0
  258. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/traces.py +0 -0
  259. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/screens/workspaces.py +0 -0
  260. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/spans_reader.py +0 -0
  261. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/theme.py +0 -0
  262. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/tool_format.py +0 -0
  263. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/turn_coordinator.py +0 -0
  264. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/turn_lifecycle.py +0 -0
  265. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/turn_state_phase.py +0 -0
  266. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/update_check.py +0 -0
  267. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/agent_dialog.py +0 -0
  268. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/agent_suggester.py +0 -0
  269. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/composer.py +0 -0
  270. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/context_bar.py +0 -0
  271. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/flash.py +0 -0
  272. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/header_bar.py +0 -0
  273. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/help_panel.py +0 -0
  274. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/mention_overlay.py +0 -0
  275. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/message_queue.py +0 -0
  276. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/overlay_mixin.py +0 -0
  277. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/permission_prompt.py +0 -0
  278. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/profile_dialog.py +0 -0
  279. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/prompt_info.py +0 -0
  280. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/session_sidebar.py +0 -0
  281. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/skills_dialog.py +0 -0
  282. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/slash_overlay.py +0 -0
  283. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/status_bar.py +0 -0
  284. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/throbber.py +0 -0
  285. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/tool_progress.py +0 -0
  286. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/tools_dialog.py +0 -0
  287. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/welcome.py +0 -0
  288. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/app/tui/widgets/whoami.py +0 -0
  289. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/agents/dreadnode.md +0 -0
  290. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/capability.yaml +0 -0
  291. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/SKILL.md +0 -0
  292. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/capability-components.md +0 -0
  293. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/capability-improvement.md +0 -0
  294. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/skills/creating-capabilities/runtime-default-capability.md +0 -0
  295. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/SKILL.md +0 -0
  296. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/command-groups.md +0 -0
  297. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/tui-crosswalk.md +0 -0
  298. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-runtime-reference/SKILL.md +0 -0
  299. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/builtin_capabilities/dreadnode/system-prompt.md +0 -0
  300. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/capabilities/__init__.py +0 -0
  301. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/capabilities/flags.py +0 -0
  302. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/capabilities/sync.py +0 -0
  303. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/capabilities/types.py +0 -0
  304. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/conditions.py +0 -0
  305. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/discovery.py +0 -0
  306. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/exceptions.py +0 -0
  307. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/execution.py +0 -0
  308. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/hook.py +0 -0
  309. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/judge.py +0 -0
  310. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/load.py +0 -0
  311. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/log.py +0 -0
  312. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/meta/__init__.py +0 -0
  313. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/meta/context.py +0 -0
  314. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/meta/hydrate.py +0 -0
  315. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/meta/introspect.py +0 -0
  316. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/object.py +0 -0
  317. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/serialization.py +0 -0
  318. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/stopping.py +0 -0
  319. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/transforms.py +0 -0
  320. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/__init__.py +0 -0
  321. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/audio.py +0 -0
  322. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/base.py +0 -0
  323. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/common.py +0 -0
  324. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/image.py +0 -0
  325. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/object_3d.py +0 -0
  326. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/table.py +0 -0
  327. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/text.py +0 -0
  328. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/types/video.py +0 -0
  329. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/core/util.py +0 -0
  330. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/datasets/__init__.py +0 -0
  331. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/datasets/dataset.py +0 -0
  332. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/datasets/hf.py +0 -0
  333. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/datasets/local.py +0 -0
  334. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/evaluations/__init__.py +0 -0
  335. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/evaluations/console.py +0 -0
  336. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/evaluations/evaluation.py +0 -0
  337. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/evaluations/events.py +0 -0
  338. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/evaluations/format.py +0 -0
  339. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/evaluations/result.py +0 -0
  340. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/evaluations/sample.py +0 -0
  341. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/__init__.py +0 -0
  342. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/chat.py +0 -0
  343. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/data.py +0 -0
  344. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/exceptions.py +0 -0
  345. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/generator/__init__.py +0 -0
  346. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/generator/http.py +0 -0
  347. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/generator/transformers_.py +0 -0
  348. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/generator/vllm_.py +0 -0
  349. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/models.py +0 -0
  350. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/parsing.py +0 -0
  351. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/tokenizer/__init__.py +0 -0
  352. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/tokenizer/base.py +0 -0
  353. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/tokenizer/transformers_.py +0 -0
  354. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/generators/utils.py +0 -0
  355. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/models/__init__.py +0 -0
  356. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/models/hf.py +0 -0
  357. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/models/local.py +0 -0
  358. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/models/model.py +0 -0
  359. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/__init__.py +0 -0
  360. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/adapters/__init__.py +0 -0
  361. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/adapters/agent.py +0 -0
  362. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/adapters/stack.py +0 -0
  363. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/api.py +0 -0
  364. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/backends/__init__.py +0 -0
  365. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/backends/base.py +0 -0
  366. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/backends/gepa.py +0 -0
  367. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/collectors.py +0 -0
  368. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/config.py +0 -0
  369. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/console.py +0 -0
  370. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/events.py +0 -0
  371. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/format.py +0 -0
  372. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/jobs.py +0 -0
  373. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/result.py +0 -0
  374. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/sampler.py +0 -0
  375. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/sampling.py +0 -0
  376. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/search.py +0 -0
  377. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/stopping.py +0 -0
  378. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/study.py +0 -0
  379. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/optimization/trial.py +0 -0
  380. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/packaging/__init__.py +0 -0
  381. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/packaging/loader.py +0 -0
  382. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/packaging/manifest.py +0 -0
  383. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/packaging/package.py +0 -0
  384. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/packaging/task_validation.py +0 -0
  385. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/py.typed +0 -0
  386. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/__init__.py +0 -0
  387. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/boundary.py +0 -0
  388. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/fuzzing.py +0 -0
  389. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/graph.py +0 -0
  390. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/grid.py +0 -0
  391. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/image.py +0 -0
  392. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/mapelites.py +0 -0
  393. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/optuna.py +0 -0
  394. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/random.py +0 -0
  395. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/registry.py +0 -0
  396. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/samplers/strategy.py +0 -0
  397. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/agent_security.py +0 -0
  398. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/agentic.py +0 -0
  399. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/agentic_workflow.py +0 -0
  400. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/classification.py +0 -0
  401. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/consistency.py +0 -0
  402. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/contains.py +0 -0
  403. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/cosine_sim.py +0 -0
  404. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/credentials.py +0 -0
  405. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/crucible.py +0 -0
  406. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/documentation_security.py +0 -0
  407. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/exfiltration_detection.py +0 -0
  408. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/format.py +0 -0
  409. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/harm.py +0 -0
  410. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/ide_security.py +0 -0
  411. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/image.py +0 -0
  412. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/json.py +0 -0
  413. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/judge.py +0 -0
  414. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/length.py +0 -0
  415. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/lexical.py +0 -0
  416. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/memorization.py +0 -0
  417. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/multi_agent_security.py +0 -0
  418. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/pii.py +0 -0
  419. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/prompt_leak.py +0 -0
  420. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/readability.py +0 -0
  421. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/sentiment.py +0 -0
  422. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/scorers/similarity.py +0 -0
  423. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/storage/__init__.py +0 -0
  424. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/storage/providers.py +0 -0
  425. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/storage/session_store.py +0 -0
  426. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/storage/storage.py +0 -0
  427. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/__init__.py +0 -0
  428. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/_ripgrep.py +0 -0
  429. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/apply_patch.py +0 -0
  430. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/dreadnode_cli.py +0 -0
  431. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/editing.py +0 -0
  432. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/execute.py +0 -0
  433. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/fetch.py +0 -0
  434. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/glob.py +0 -0
  435. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/grep.py +0 -0
  436. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/interaction.py +0 -0
  437. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/ls.py +0 -0
  438. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/memory.py +0 -0
  439. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/read.py +0 -0
  440. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/report.py +0 -0
  441. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/task.py +0 -0
  442. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/think.py +0 -0
  443. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/todo.py +0 -0
  444. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/trajectory_search.py +0 -0
  445. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/web_search.py +0 -0
  446. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tools/write.py +0 -0
  447. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tracing/__init__.py +0 -0
  448. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tracing/constants.py +0 -0
  449. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tracing/convert.py +0 -0
  450. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tracing/exporter.py +0 -0
  451. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tracing/exporters.py +0 -0
  452. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tracing/span.py +0 -0
  453. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tracing/spans.py +0 -0
  454. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/tracing/trace_converter.py +0 -0
  455. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/__init__.py +0 -0
  456. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/base.py +0 -0
  457. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/dpo.py +0 -0
  458. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/etl/__init__.py +0 -0
  459. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/etl/_common.py +0 -0
  460. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/etl/rl.py +0 -0
  461. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/etl/sft.py +0 -0
  462. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/etl/worlds.py +0 -0
  463. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/events.py +0 -0
  464. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/grpo.py +0 -0
  465. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/jobs.py +0 -0
  466. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ppo.py +0 -0
  467. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/prime.py +0 -0
  468. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/__init__.py +0 -0
  469. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/async_trainer.py +0 -0
  470. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/config.py +0 -0
  471. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/coordinator.py +0 -0
  472. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/distributed.py +0 -0
  473. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/dpo.py +0 -0
  474. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/experience.py +0 -0
  475. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/fsdp2_learner.py +0 -0
  476. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/inference.py +0 -0
  477. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/learner.py +0 -0
  478. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/multi_turn.py +0 -0
  479. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/ppo.py +0 -0
  480. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/reward_model.py +0 -0
  481. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/rollout_env.py +0 -0
  482. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/rollout_worker.py +0 -0
  483. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/sft.py +0 -0
  484. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/ray/trainer.py +0 -0
  485. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/recipes.py +0 -0
  486. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rewards/__init__.py +0 -0
  487. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rewards/aggregator.py +0 -0
  488. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rewards/functions.py +0 -0
  489. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rewards/scorer_bridge.py +0 -0
  490. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rewards/shaping.py +0 -0
  491. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rewards/types.py +0 -0
  492. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rollouts/__init__.py +0 -0
  493. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rollouts/adapters.py +0 -0
  494. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rollouts/orchestrator.py +0 -0
  495. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rollouts/types.py +0 -0
  496. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/rollouts/worlds.py +0 -0
  497. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/serving/__init__.py +0 -0
  498. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/serving/vllm_client.py +0 -0
  499. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/sft.py +0 -0
  500. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/tinker/__init__.py +0 -0
  501. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/tinker/config.py +0 -0
  502. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/tinker/data.py +0 -0
  503. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/tinker/renderer.py +0 -0
  504. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/tinker/rl.py +0 -0
  505. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/tinker/trainer.py +0 -0
  506. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/tinker_sft.py +0 -0
  507. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/training/utils.py +0 -0
  508. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/agent_skill.py +0 -0
  509. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/audio.py +0 -0
  510. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/constitutional.py +0 -0
  511. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/document.py +0 -0
  512. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/documentation_poison.py +0 -0
  513. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/exfiltration.py +0 -0
  514. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/flip_attack.py +0 -0
  515. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/guardrail_bypass.py +0 -0
  516. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/ide_injection.py +0 -0
  517. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/injection.py +0 -0
  518. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/json_tools.py +0 -0
  519. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/language.py +0 -0
  520. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/logic_bomb.py +0 -0
  521. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/perturbation.py +0 -0
  522. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/pii_extraction.py +0 -0
  523. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/pythonic_tools.py +0 -0
  524. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/refine.py +0 -0
  525. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/response_steering.py +0 -0
  526. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/stylistic.py +0 -0
  527. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/substitution.py +0 -0
  528. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/swap.py +0 -0
  529. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/system_prompt_extraction.py +0 -0
  530. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/text.py +0 -0
  531. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/transforms/video.py +0 -0
  532. {dreadnode-2.0.13 → dreadnode-2.0.14}/dreadnode/version.py +0 -0
@@ -215,3 +215,7 @@ __marimo__/
215
215
  # Streamlit
216
216
  .streamlit/secrets.toml
217
217
  .claude/
218
+
219
+ # Local experiment notebooks
220
+ examples/notebooks/cricket/
221
+ examples/notebooks/llama-scout/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dreadnode
3
- Version: 2.0.13
3
+ Version: 2.0.14
4
4
  Summary: Dreadnode SDK
5
5
  Project-URL: Homepage, https://dreadnode.io
6
6
  Project-URL: Documentation, https://docs.dreadnode.io
@@ -384,9 +384,9 @@ uv sync --all-extras
384
384
 
385
385
  ## Documentation
386
386
 
387
- - **[Installation Guide](https://docs.dreadnode.io/strikes/install)** - Setup options
388
- - **[Introduction](https://docs.dreadnode.io/strikes/intro)** - Getting started guide
389
- - **[API Reference](https://docs.dreadnode.io/strikes/api)** - Complete API documentation
387
+ - **[Getting started](https://docs.dreadnode.io/getting-started/overview/)** - Install, authenticate, first run
388
+ - **[Quickstart](https://docs.dreadnode.io/getting-started/quickstart/)** - End-to-end walkthrough
389
+ - **[SDK reference](https://docs.dreadnode.io/sdk/overview/)** - Complete SDK reference
390
390
 
391
391
  ## License
392
392
 
@@ -312,9 +312,9 @@ uv sync --all-extras
312
312
 
313
313
  ## Documentation
314
314
 
315
- - **[Installation Guide](https://docs.dreadnode.io/strikes/install)** - Setup options
316
- - **[Introduction](https://docs.dreadnode.io/strikes/intro)** - Getting started guide
317
- - **[API Reference](https://docs.dreadnode.io/strikes/api)** - Complete API documentation
315
+ - **[Getting started](https://docs.dreadnode.io/getting-started/overview/)** - Install, authenticate, first run
316
+ - **[Quickstart](https://docs.dreadnode.io/getting-started/quickstart/)** - End-to-end walkthrough
317
+ - **[SDK reference](https://docs.dreadnode.io/sdk/overview/)** - Complete SDK reference
318
318
 
319
319
  ## License
320
320
 
@@ -166,6 +166,7 @@ __all__ = [
166
166
  "tag",
167
167
  "task",
168
168
  "task_and_run",
169
+ "task_env",
169
170
  "task_span",
170
171
  "tool",
171
172
  "tool_method",
@@ -260,6 +261,7 @@ __instance_methods__: list[str] = [
260
261
  "task_span",
261
262
  "run",
262
263
  "task_and_run",
264
+ "task_env",
263
265
  "scorer",
264
266
  "evaluation",
265
267
  "optimize_anything",
@@ -153,7 +153,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
153
153
  instructions: t.Annotated[str | None, AfterValidator(lambda x: dedent(x) if x else x)] = Config(
154
154
  default=None
155
155
  )
156
- cache: caching.CacheMode | None = Config(default=None, repr=False)
156
+ cache: caching.CacheMode | None = Config(default="latest", repr=False)
157
157
  tools: list[Tool | Toolset] = Config(default_factory=list, validate_default=False)
158
158
  tool_mode: ToolMode = Config(default="auto", repr=False)
159
159
  hooks: list[Hook] = Config(default_factory=list, repr=False)
@@ -307,7 +307,8 @@ class Agent(Executor[AgentEvent, Trajectory]):
307
307
  post_transforms.append(post_transform)
308
308
 
309
309
  try:
310
- messages = caching.apply_cache_mode_to_messages(self.cache, [messages])[0]
310
+ if self.cache is not None and self.generator.supports_prompt_caching():
311
+ messages = caching.apply_cache_mode_to_messages(self.cache, [messages])[0]
311
312
  logger.trace(f"Generating with model '{self.generator.model}'. Messages: {messages!r}")
312
313
 
313
314
  generated = (await self.generator.generate_messages([messages], [params]))[0]
@@ -494,9 +495,18 @@ class Agent(Executor[AgentEvent, Trajectory]):
494
495
  )
495
496
 
496
497
  if not _is_context_length_error(error):
498
+ logger.debug(
499
+ "Overflow recovery: error not classified as context-length ({}: {}); skipping",
500
+ type(error).__name__,
501
+ str(error)[:200],
502
+ )
497
503
  return None
498
504
 
499
505
  if len(messages) <= 10:
506
+ logger.info(
507
+ "Overflow recovery: too few messages to compact ({} <= 10); skipping",
508
+ len(messages),
509
+ )
500
510
  return None
501
511
 
502
512
  work = list(messages)
@@ -504,6 +514,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
504
514
 
505
515
  summarizer = self._generator
506
516
  if summarizer is None:
517
+ logger.warning("Overflow recovery: no summarizer generator available; skipping")
507
518
  return None
508
519
 
509
520
  # Cap summarizer input at ~60% of the model's token budget (~3 chars/token,
@@ -519,12 +530,27 @@ class Agent(Executor[AgentEvent, Trajectory]):
519
530
  max_summarize_chars=max_summarize_chars,
520
531
  )
521
532
  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
- )
533
+ # Distinguish "no safe boundary exists" (neither a simple-assistant
534
+ # nor a complete tool-call group anywhere in the trajectory) from
535
+ # "boundaries exist but none fit the summarizer budget". Both
536
+ # currently return 0; telling them apart is the diagnostic split
537
+ # point ENG-6545 needs.
538
+ uncapped_boundary = find_summarization_boundary(work, min_messages_to_keep=10)
539
+ if uncapped_boundary == 0:
540
+ logger.info(
541
+ "Overflow recovery: no API-safe boundary in {} messages "
542
+ "(no simple-assistant or complete tool-call group); skipping",
543
+ len(work),
544
+ )
545
+ else:
546
+ logger.info(
547
+ "Overflow recovery: valid boundary at index {} exceeds summarizer "
548
+ "budget (messages={}, budget_tokens={}, max_chars={}); skipping",
549
+ uncapped_boundary,
550
+ len(work),
551
+ budget_tokens,
552
+ max_summarize_chars,
553
+ )
528
554
  return None
529
555
 
530
556
  to_summarize = work[:boundary]
@@ -578,7 +604,7 @@ class Agent(Executor[AgentEvent, Trajectory]):
578
604
  async for event in self._dispatch(start_event):
579
605
  yield event
580
606
 
581
- tool = next((t for t in self.all_tools if t.name == tool_call.name), None)
607
+ tool = next((t for t in self.all_tools if t.wire_name == tool_call.name), None)
582
608
 
583
609
  if tool is None:
584
610
  error_msg = f"Tool '{tool_call.name}' not found."
@@ -620,6 +646,14 @@ class Agent(Executor[AgentEvent, Trajectory]):
620
646
  try:
621
647
  message, stop = await tool.handle_tool_call(tool_call)
622
648
 
649
+ # Tools that catch their own exceptions (bash non-zero,
650
+ # @tool(catch=True), MCP isError) lift the failure into
651
+ # message.metadata. Surface it on ToolEnd so renderers can
652
+ # mark the call as errored without needing to parse XML
653
+ # out of the result body.
654
+ tool_error = message.metadata.get("error")
655
+ tool_error_type = message.metadata.get("error_type")
656
+
623
657
  end_event = ToolEnd(
624
658
  agent_id=self.agent_id,
625
659
  agent_name=self.name,
@@ -627,11 +661,19 @@ class Agent(Executor[AgentEvent, Trajectory]):
627
661
  result=message.content,
628
662
  output_file=message.metadata.get("output_file"),
629
663
  stop=stop,
664
+ error=tool_error,
665
+ error_type=tool_error_type,
630
666
  )
631
667
  async for event in self._dispatch(end_event):
632
668
  yield event
633
669
  end_event.emit(t_span) # Emit after dispatch - metrics attached
634
670
 
671
+ # ToolStep.error is typed for actual exceptions (uncaught
672
+ # errors that get re-raised). The metadata error string is
673
+ # already on ToolEnd.error above — don't double-emit it
674
+ # here, or Pydantic will reject the str input and the
675
+ # raised ValidationError will surface as a duplicate
676
+ # ToolError row in the TUI.
635
677
  step_event = ToolStep(
636
678
  agent_id=self.agent_id,
637
679
  agent_name=self.name,
@@ -799,6 +841,12 @@ class Agent(Executor[AgentEvent, Trajectory]):
799
841
  yield event
800
842
  messages = recovered_messages
801
843
  step_chat = await self._generate(messages)
844
+ if step_chat.failed and step_chat.error:
845
+ logger.warning(
846
+ "Overflow recovery: compacted regenerate also failed ({}: {})",
847
+ type(step_chat.error).__name__,
848
+ str(step_chat.error)[:200],
849
+ )
802
850
 
803
851
  if step_chat.failed and step_chat.error:
804
852
  from dreadnode.agents.hooks import _describe_generation_error
@@ -212,7 +212,7 @@ class AgentStart(AgentEvent):
212
212
 
213
213
  goal = self.inputs.get("goal")
214
214
  if goal is not None:
215
- span.set_attribute(AGENT_ATTRIBUTE_GOAL, str(goal)[:2000])
215
+ span.set_attribute(AGENT_ATTRIBUTE_GOAL, str(goal))
216
216
  span.log_input(name="goal", value=goal)
217
217
 
218
218
  if session_id := self.params.get("session_id"):
@@ -259,7 +259,7 @@ class AgentEnd(AgentEvent):
259
259
 
260
260
  def emit(self, span: "TaskSpan") -> None:
261
261
  if self.error:
262
- span.log_output(name="error", value=str(self.error)[:500])
262
+ span.log_output(name="error", value=str(self.error))
263
263
 
264
264
  super().emit(span)
265
265
 
@@ -338,11 +338,7 @@ class GenerationRetry(AgentEvent):
338
338
 
339
339
 
340
340
  class AgentStalled(AgentEvent):
341
- """Event: The agent is stalled and there are no tool calls, or stop condition).
342
-
343
- Attributes:
344
- None
345
- """
341
+ """Event: The agent is stalled and there are no tool calls, or stop condition)."""
346
342
 
347
343
  def _get_data(self) -> dict[str, t.Any]:
348
344
  return {"reason": "No tool calls and no stop condition met"}
@@ -516,16 +512,26 @@ class ToolStart(AgentEvent):
516
512
  class ToolEnd(AgentEvent):
517
513
  """Event: A tool call has completed.
518
514
 
515
+ A non-empty ``error`` means the tool ran to completion but reported
516
+ a failure (e.g. bash non-zero exit, ``@tool(catch=True)`` swallowing
517
+ an exception, or an MCP server returning ``isError=true``). Uncaught
518
+ exceptions go through :class:`ToolError` instead.
519
+
519
520
  Attributes:
520
521
  tool_call: The tool call that was completed.
521
522
  result: The result returned by the tool, if applicable.
522
523
  stop: Whether this tool requested the agent to stop.
524
+ error: A failure message lifted from ``message.metadata['error']``.
525
+ error_type: Exception class name when the error was sourced from
526
+ an :class:`ErrorModel` carrying that metadata.
523
527
  """
524
528
 
525
529
  tool_call: ToolCall
526
530
  result: str | None = None
527
531
  stop: bool = False
528
532
  output_file: str | None = None
533
+ error: str | None = None
534
+ error_type: str | None = None
529
535
 
530
536
  def _get_data(self) -> dict[str, t.Any]:
531
537
  return {
@@ -533,9 +539,11 @@ class ToolEnd(AgentEvent):
533
539
  "id": self.tool_call.id,
534
540
  "name": self.tool_call.name,
535
541
  },
536
- "result": self.result[:4000] if self.result else None,
542
+ "result": self.result or None,
537
543
  "output_file": self.output_file,
538
544
  "stop": self.stop,
545
+ "error": self.error,
546
+ "error_type": self.error_type,
539
547
  }
540
548
 
541
549
  def __repr__(self) -> str:
@@ -545,11 +553,15 @@ class ToolEnd(AgentEvent):
545
553
  def emit(self, span: "TaskSpan") -> None:
546
554
  # Attributes
547
555
  if self.result:
548
- span.set_attribute(TOOL_ATTRIBUTE_RESULT, self.result[:4000])
549
- span.log_output("result", self.result[:4000])
556
+ span.set_attribute(TOOL_ATTRIBUTE_RESULT, self.result)
557
+ span.log_output("result", self.result)
550
558
 
551
559
  span.set_attribute(TOOL_ATTRIBUTE_STOPPED, self.stop)
552
560
 
561
+ if self.error:
562
+ span.set_attribute(TOOL_ATTRIBUTE_ERROR, self.error)
563
+ span.log_metric(f"tool/{self.tool_call.name}/error", 1)
564
+
553
565
  super().emit(span)
554
566
 
555
567
  def format_as_panel(self, *, truncate: bool = False) -> Panel:
@@ -591,7 +603,7 @@ class ToolError(AgentEvent):
591
603
 
592
604
  def emit(self, span: "TaskSpan") -> None:
593
605
  # Attributes
594
- span.set_attribute(TOOL_ATTRIBUTE_ERROR, str(self.error)[:1000])
606
+ span.set_attribute(TOOL_ATTRIBUTE_ERROR, str(self.error))
595
607
 
596
608
  # Metrics
597
609
  span.log_metric(f"tool/{self.tool_call.name}/error", 1)
@@ -666,7 +678,7 @@ class GenerationStep(AgentStep):
666
678
 
667
679
  if self.messages:
668
680
  last_msg = self.messages[-1]
669
- content = str(last_msg.content)[:4000] if last_msg.content else None
681
+ content = str(last_msg.content) if last_msg.content else None
670
682
 
671
683
  for tc in last_msg.tool_calls or []:
672
684
  try:
@@ -721,7 +733,7 @@ class GenerationStep(AgentStep):
721
733
  span.set_attribute(GENERATION_ATTRIBUTE_ROLE, last_msg.role)
722
734
 
723
735
  if last_msg.content:
724
- span.set_attribute(GENERATION_ATTRIBUTE_CONTENT, str(last_msg.content)[:4000])
736
+ span.set_attribute(GENERATION_ATTRIBUTE_CONTENT, str(last_msg.content))
725
737
 
726
738
  if last_msg.tool_calls:
727
739
  span.set_attribute(
@@ -780,7 +792,7 @@ class GenerationStart(AgentEvent):
780
792
  for m in self.messages:
781
793
  msg: dict[str, t.Any] = {"role": m.role}
782
794
  if m.content:
783
- msg["content"] = str(m.content)[:4000]
795
+ msg["content"] = str(m.content)
784
796
  if m.tool_calls:
785
797
  msg["tool_calls"] = [{"name": tc.name, "id": tc.id} for tc in m.tool_calls]
786
798
  input_messages.append(msg)
@@ -806,7 +818,7 @@ class GenerationEnd(AgentStep):
806
818
 
807
819
  if self.messages:
808
820
  last_msg = self.messages[-1]
809
- content = str(last_msg.content)[:4000] if last_msg.content else None
821
+ content = str(last_msg.content) if last_msg.content else None
810
822
 
811
823
  for tc in last_msg.tool_calls or []:
812
824
  try:
@@ -849,7 +861,7 @@ class GenerationEnd(AgentStep):
849
861
  span.set_attribute(GENERATION_ATTRIBUTE_ROLE, last_msg.role)
850
862
 
851
863
  if last_msg.content:
852
- span.set_attribute(GENERATION_ATTRIBUTE_CONTENT, str(last_msg.content)[:4000])
864
+ span.set_attribute(GENERATION_ATTRIBUTE_CONTENT, str(last_msg.content))
853
865
  span.log_output(
854
866
  name="generation",
855
867
  value=last_msg.content,
@@ -243,8 +243,16 @@ def find_summarization_boundary(
243
243
 
244
244
  Walks messages from the start and enumerates every safe split point that
245
245
  leaves at least ``min_messages_to_keep`` messages in the "keep" portion.
246
- A safe boundary is after a simple assistant message (no tool calls)
247
- this is the natural end of a complete conversational turn.
246
+ A boundary is safe when both sides of the cut are API-valid chat
247
+ sequences no orphaned ``tool_calls`` and no orphaned ``tool`` responses.
248
+ Two kinds of positions qualify:
249
+
250
+ - **After a simple assistant message** (no ``tool_calls``) — the natural
251
+ end of a complete conversational turn.
252
+ - **After a complete tool-call group** — every ``tool_call.id`` from a
253
+ preceding ``assistant`` message has a matching ``tool`` response. The
254
+ cut falls after the last matching tool response, so neither side has a
255
+ dangling tool call or result.
248
256
 
249
257
  When ``max_summarize_chars`` is provided, returns the largest safe split
250
258
  whose cumulative ``len(str(message))`` stays within the cap. This keeps
@@ -264,15 +272,28 @@ def find_summarization_boundary(
264
272
  # (0, 0) is always a valid "no compaction" candidate.
265
273
  candidates: list[tuple[int, int]] = [(0, 0)]
266
274
  running_chars = 0
275
+ # Tool-call ids from the most recent assistant(tool_calls) that have not
276
+ # yet been resolved by matching tool responses. When empty, the preceding
277
+ # tool-call group is complete and the position is API-safe to cut at.
278
+ pending_tool_ids: set[str] = set()
267
279
  for i, message in enumerate(messages):
268
280
  if len(messages) - i <= min_messages_to_keep:
269
281
  break
270
282
  running_chars += len(str(message))
271
- is_simple_assistant = message.role == "assistant" and not getattr(
272
- message, "tool_calls", None
273
- )
274
- if is_simple_assistant:
275
- candidates.append((i + 1, running_chars))
283
+ if message.role == "assistant":
284
+ tool_calls = getattr(message, "tool_calls", None)
285
+ if tool_calls:
286
+ pending_tool_ids = {tc.id for tc in tool_calls}
287
+ elif not pending_tool_ids:
288
+ candidates.append((i + 1, running_chars))
289
+ elif (
290
+ message.role == "tool"
291
+ and getattr(message, "tool_call_id", None)
292
+ and message.tool_call_id in pending_tool_ids
293
+ ):
294
+ pending_tool_ids.discard(message.tool_call_id)
295
+ if not pending_tool_ids:
296
+ candidates.append((i + 1, running_chars))
276
297
 
277
298
  if max_summarize_chars is None:
278
299
  return candidates[-1][0]
@@ -29,6 +29,8 @@ from dreadnode.agents.mcp.config import (
29
29
  Transport,
30
30
  )
31
31
  from dreadnode.agents.tools import Tool
32
+ from dreadnode.app.paths import rotate_log
33
+ from dreadnode.generators.models import ErrorModel
32
34
 
33
35
  if t.TYPE_CHECKING:
34
36
  from mcp import ClientSession
@@ -44,20 +46,36 @@ class _StderrCapture:
44
46
  letting it spill into the parent terminal. On connection failure the
45
47
  buffered lines are included in the error message so users can see
46
48
  why the server process crashed.
49
+
50
+ When a ``log_path`` is provided, each captured line is also tee'd to
51
+ disk so operators can ``tail -f`` the server's stderr — matching the
52
+ subprocess-worker convention under ``~/.dreadnode/logs/``.
47
53
  """
48
54
 
49
55
  _MAX_LINES = 50
50
56
 
51
- def __init__(self, read_fd: int, exit_stack: AsyncExitStack) -> None:
57
+ def __init__(
58
+ self,
59
+ read_fd: int,
60
+ exit_stack: AsyncExitStack,
61
+ *,
62
+ log_path: Path | None = None,
63
+ ) -> None:
52
64
  import os
53
65
  import threading
54
66
 
55
67
  self._lines: list[str] = []
56
68
  self._read_file = os.fdopen(read_fd, "r")
69
+ self._log_file = self._open_log_file(log_path)
57
70
 
58
71
  def _reader() -> None:
59
72
  try:
60
73
  for raw in self._read_file:
74
+ if self._log_file is not None:
75
+ # Don't let a disk-side failure break the in-memory
76
+ # buffer or the connection itself.
77
+ with contextlib.suppress(OSError):
78
+ self._log_file.write(raw)
61
79
  line = raw.rstrip("\n")
62
80
  if line:
63
81
  logger.debug("MCP stderr: {}", line)
@@ -71,9 +89,36 @@ class _StderrCapture:
71
89
  self._thread.start()
72
90
  exit_stack.callback(self._close)
73
91
 
92
+ @staticmethod
93
+ def _open_log_file(log_path: Path | None) -> t.TextIO | None:
94
+ """Open the stderr log file for writing, or ``None`` on failure.
95
+
96
+ Rotates the prior log to ``<path>.prev`` first, so a failed connect
97
+ leaves the previous session's stderr intact for diffing. Truncates
98
+ on open, line-buffered so ``tail -f`` reflects new output
99
+ immediately.
100
+ """
101
+ if log_path is None:
102
+ return None
103
+ rotate_log(log_path)
104
+ try:
105
+ log_path.parent.mkdir(parents=True, exist_ok=True)
106
+ return log_path.open("w", buffering=1, encoding="utf-8", errors="replace")
107
+ except OSError as exc:
108
+ logger.warning(
109
+ "MCP stderr capture: failed to open log file '{}' ({}); keeping in-memory buffer only",
110
+ log_path,
111
+ exc,
112
+ )
113
+ return None
114
+
74
115
  def _close(self) -> None:
75
116
  with contextlib.suppress(OSError):
76
117
  self._read_file.close()
118
+ if self._log_file is not None:
119
+ with contextlib.suppress(OSError):
120
+ self._log_file.close()
121
+ self._log_file = None
77
122
  self._thread.join(timeout=1)
78
123
 
79
124
  @property
@@ -156,6 +201,7 @@ class MCPClient:
156
201
  *,
157
202
  oauth: t.Any = None,
158
203
  init_timeout: float = DEFAULT_INIT_TIMEOUT,
204
+ log_path: Path | None = None,
159
205
  ) -> None:
160
206
  # Handle deprecated "sse" transport
161
207
  if transport == "sse":
@@ -176,6 +222,9 @@ class MCPClient:
176
222
  self._session = None
177
223
  self._oauth_config = oauth
178
224
  self._init_timeout = init_timeout
225
+ # Only meaningful for stdio transports — the stderr capture wires it
226
+ # into _StderrCapture on connect. HTTP transports ignore it.
227
+ self._log_path = log_path
179
228
  self._stderr_capture: _StderrCapture | None = None
180
229
  self._owner_task = None
181
230
  self._shutdown_event = None
@@ -183,8 +232,13 @@ class MCPClient:
183
232
  self._lifecycle_lock = None
184
233
 
185
234
  @classmethod
186
- def from_config(cls, config: ServerConfig) -> "MCPClient":
187
- """Create a client from a typed server config."""
235
+ def from_config(cls, config: ServerConfig, *, log_path: Path | None = None) -> "MCPClient":
236
+ """Create a client from a typed server config.
237
+
238
+ The SDK's MCP lifecycle manager passes ``log_path`` to tee stderr
239
+ under ``~/.dreadnode/logs/``. User-code callers of
240
+ :func:`dreadnode.agents.mcp` don't need to supply it.
241
+ """
188
242
  if isinstance(config, StdioServerConfig):
189
243
  connection: StdioConnection = StdioConnection(
190
244
  command=config.command,
@@ -192,7 +246,12 @@ class MCPClient:
192
246
  cwd=config.cwd,
193
247
  env=config.env,
194
248
  )
195
- return cls("stdio", connection, init_timeout=config.init_timeout)
249
+ return cls(
250
+ "stdio",
251
+ connection,
252
+ init_timeout=config.init_timeout,
253
+ log_path=log_path,
254
+ )
196
255
 
197
256
  # HttpServerConfig
198
257
  connection_dict: dict[str, t.Any] = {
@@ -206,6 +265,7 @@ class MCPClient:
206
265
  connection_dict,
207
266
  oauth=config.oauth,
208
267
  init_timeout=config.init_timeout,
268
+ log_path=log_path,
209
269
  )
210
270
 
211
271
  @property
@@ -217,6 +277,27 @@ class MCPClient:
217
277
  """Error message if status is FAILED or NEEDS_AUTH."""
218
278
  return self._error
219
279
 
280
+ @property
281
+ def log_path(self) -> Path | None:
282
+ """Path that stderr is tee'd to, or ``None`` if capture is in-memory only.
283
+
284
+ Only populated for stdio transports; HTTP transports don't spawn a
285
+ subprocess and have nothing to capture.
286
+ """
287
+ return self._log_path
288
+
289
+ @property
290
+ def recent_stderr(self) -> list[str]:
291
+ """Captured stderr lines from the subprocess, bounded by the ring buffer.
292
+
293
+ Mirrors :attr:`SubprocessWorkerRunner.recent_output` so the TUI can
294
+ render the same progressive-disclosure block for MCP servers and
295
+ workers. Empty for HTTP transports or before :meth:`connect` runs.
296
+ """
297
+ if self._stderr_capture is None:
298
+ return []
299
+ return self._stderr_capture.last_lines
300
+
220
301
  @property
221
302
  def session(self) -> "ClientSession":
222
303
  if self._session is None:
@@ -226,6 +307,21 @@ class MCPClient:
226
307
  def _make_execute_on_server(self, tool_name: str) -> t.Callable[..., t.Any]:
227
308
  async def execute_on_server(**kwargs: t.Any) -> t.Any:
228
309
  result = await self.session.call_tool(tool_name, kwargs)
310
+ # Protocol-level failures (``isError=true``) are reported as a
311
+ # successful response carrying error content. Lift them into
312
+ # an ``ErrorModel`` so ``Tool.handle_tool_call`` routes the
313
+ # message through the same ``metadata['error']`` path used by
314
+ # ``@tool(catch=True)`` failures (e.g. bash non-zero exits).
315
+ if result.isError:
316
+ error_text = (
317
+ "\n".join(
318
+ content.text # ty: ignore[unresolved-attribute]
319
+ for content in result.content
320
+ if content.type == "text"
321
+ )
322
+ or f"MCP tool '{tool_name}' reported an error."
323
+ )
324
+ return ErrorModel(content=error_text, type="MCPToolError")
229
325
  return _convert_mcp_result_to_message_parts(result)
230
326
 
231
327
  return execute_on_server
@@ -333,7 +429,7 @@ class MCPClient:
333
429
  read_fd, write_fd = os.pipe()
334
430
  write_file = os.fdopen(write_fd, "w")
335
431
 
336
- stderr_capture = _StderrCapture(read_fd, self._exit_stack)
432
+ stderr_capture = _StderrCapture(read_fd, self._exit_stack, log_path=self._log_path)
337
433
  self._stderr_capture = stderr_capture
338
434
 
339
435
  server_params = StdioServerParameters(**connection)