decodingtrust-agent-sdk 0.1.0__py3-none-any.whl

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 (374) hide show
  1. agent/__init__.py +30 -0
  2. agent/claudesdk/__init__.py +8 -0
  3. agent/claudesdk/example.py +221 -0
  4. agent/claudesdk/src/__init__.py +8 -0
  5. agent/claudesdk/src/agent.py +400 -0
  6. agent/claudesdk/src/mcp_proxy.py +409 -0
  7. agent/claudesdk/src/utils.py +420 -0
  8. agent/googleadk/__init__.py +15 -0
  9. agent/googleadk/example.py +237 -0
  10. agent/googleadk/src/__init__.py +12 -0
  11. agent/googleadk/src/agent.py +401 -0
  12. agent/googleadk/src/mcp_wrapper.py +163 -0
  13. agent/googleadk/src/utils.py +602 -0
  14. agent/langchain/__init__.py +8 -0
  15. agent/langchain/example.py +213 -0
  16. agent/langchain/src/__init__.py +8 -0
  17. agent/langchain/src/agent.py +645 -0
  18. agent/langchain/src/utils.py +433 -0
  19. agent/openaisdk/__init__.py +17 -0
  20. agent/openaisdk/example.py +228 -0
  21. agent/openaisdk/src/__init__.py +12 -0
  22. agent/openaisdk/src/agent.py +491 -0
  23. agent/openaisdk/src/agent_wrapper.py +143 -0
  24. agent/openaisdk/src/mcp_wrapper.py +395 -0
  25. agent/openaisdk/src/utils.py +493 -0
  26. agent/openclaw/__init__.py +10 -0
  27. agent/openclaw/example.py +251 -0
  28. agent/openclaw/src/__init__.py +14 -0
  29. agent/openclaw/src/agent.py +930 -0
  30. agent/openclaw/src/helpers/__init__.py +1 -0
  31. agent/openclaw/src/helpers/auth_helpers.py +55 -0
  32. agent/openclaw/src/mcp_proxy.py +564 -0
  33. agent/openclaw/src/plugin_generator.py +231 -0
  34. agent/openclaw/src/utils.py +341 -0
  35. agent/pocketflow/__init__.py +18 -0
  36. agent/pocketflow/example.py +221 -0
  37. agent/pocketflow/prompts/react_agent.py +46 -0
  38. agent/pocketflow/src/__init__.py +6 -0
  39. agent/pocketflow/src/agent.py +507 -0
  40. agent/pocketflow/src/agent_wrapper.py +159 -0
  41. agent/pocketflow/src/async_helper.py +92 -0
  42. agent/pocketflow/src/mcp_react_agent.py +279 -0
  43. agent/pocketflow/src/native_agent.py +74 -0
  44. agent/pocketflow/src/nodes.py +467 -0
  45. benchmark/__init__.py +0 -0
  46. benchmark/browser/benign.jsonl +34 -0
  47. benchmark/browser/direct.jsonl +85 -0
  48. benchmark/browser/indirect.jsonl +82 -0
  49. benchmark/code/benign.jsonl +0 -0
  50. benchmark/code/direct.jsonl +121 -0
  51. benchmark/code/indirect.jsonl +165 -0
  52. benchmark/crm/benign.jsonl +165 -0
  53. benchmark/crm/direct.jsonl +90 -0
  54. benchmark/crm/indirect.jsonl +150 -0
  55. benchmark/customer-service/benign.jsonl +160 -0
  56. benchmark/customer-service/direct.jsonl +100 -0
  57. benchmark/customer-service/indirect.jsonl +101 -0
  58. benchmark/finance/benign.jsonl +0 -0
  59. benchmark/finance/direct.jsonl +200 -0
  60. benchmark/finance/indirect.jsonl +200 -0
  61. benchmark/legal/benign.jsonl +0 -0
  62. benchmark/legal/direct.jsonl +200 -0
  63. benchmark/legal/indirect.jsonl +200 -0
  64. benchmark/macos/benign.jsonl +30 -0
  65. benchmark/macos/direct.jsonl +50 -0
  66. benchmark/macos/indirect.jsonl +50 -0
  67. benchmark/medical/benign.jsonl +642 -0
  68. benchmark/medical/direct.jsonl +229 -0
  69. benchmark/medical/indirect.jsonl +222 -0
  70. benchmark/os-filesystem/benign.jsonl +200 -0
  71. benchmark/os-filesystem/direct.jsonl +200 -0
  72. benchmark/os-filesystem/indirect.jsonl +200 -0
  73. benchmark/research/benign.jsonl +0 -0
  74. benchmark/research/direct.jsonl +119 -0
  75. benchmark/research/indirect.jsonl +125 -0
  76. benchmark/telecom/benign.jsonl +120 -0
  77. benchmark/telecom/direct.jsonl +161 -0
  78. benchmark/telecom/indirect.jsonl +166 -0
  79. benchmark/travel/benign.jsonl +130 -0
  80. benchmark/travel/direct.jsonl +105 -0
  81. benchmark/travel/indirect.jsonl +120 -0
  82. benchmark/windows/benign.jsonl +100 -0
  83. benchmark/windows/direct.jsonl +140 -0
  84. benchmark/windows/indirect.jsonl +107 -0
  85. benchmark/workflow/benign.jsonl +335 -0
  86. benchmark/workflow/direct.jsonl +78 -0
  87. benchmark/workflow/indirect.jsonl +107 -0
  88. cli/__init__.py +5 -0
  89. cli/main.py +182 -0
  90. cli/scaffold.py +334 -0
  91. decodingtrust_agent_sdk-0.1.0.dist-info/METADATA +642 -0
  92. decodingtrust_agent_sdk-0.1.0.dist-info/RECORD +374 -0
  93. decodingtrust_agent_sdk-0.1.0.dist-info/WHEEL +5 -0
  94. decodingtrust_agent_sdk-0.1.0.dist-info/entry_points.txt +2 -0
  95. decodingtrust_agent_sdk-0.1.0.dist-info/licenses/LICENSE +201 -0
  96. decodingtrust_agent_sdk-0.1.0.dist-info/top_level.txt +6 -0
  97. dt_arena/config/env.yaml +515 -0
  98. dt_arena/config/injection_mcp.yaml +430 -0
  99. dt_arena/config/mcp.yaml +642 -0
  100. dt_arena/envs/arxiv/docker-compose-hub.yml +31 -0
  101. dt_arena/envs/arxiv/docker-compose.yml +36 -0
  102. dt_arena/envs/atlassian/docker/docker-compose.dev.yml +65 -0
  103. dt_arena/envs/atlassian/docker/docker-compose.yml +53 -0
  104. dt_arena/envs/atlassian/docker-compose-hub.yml +57 -0
  105. dt_arena/envs/atlassian/docker-compose.yml +72 -0
  106. dt_arena/envs/bigquery/docker-compose.yml +20 -0
  107. dt_arena/envs/booking/docker-compose.yml +59 -0
  108. dt_arena/envs/calendar/docker-compose-hub.yml +30 -0
  109. dt_arena/envs/calendar/docker-compose.yml +42 -0
  110. dt_arena/envs/custom-website/docker-compose.yml +6 -0
  111. dt_arena/envs/customer_service/docker-compose.yml +59 -0
  112. dt_arena/envs/databricks/docker-compose-hub.yml +47 -0
  113. dt_arena/envs/databricks/docker-compose.yml +51 -0
  114. dt_arena/envs/ecommerce/docker-compose.yml +6 -0
  115. dt_arena/envs/ers/docker-compose.yml +36 -0
  116. dt_arena/envs/ers/hrms/docker/docker-compose.yml +31 -0
  117. dt_arena/envs/finance/docker-compose.yml +23 -0
  118. dt_arena/envs/github/docker/docker-compose-hub.yml +50 -0
  119. dt_arena/envs/github/docker/docker-compose.yml +50 -0
  120. dt_arena/envs/gmail/docker-compose-hub.yml +51 -0
  121. dt_arena/envs/gmail/docker-compose.yml +65 -0
  122. dt_arena/envs/google-form/docker-compose-hub.yml +33 -0
  123. dt_arena/envs/google-form/docker-compose.yml +41 -0
  124. dt_arena/envs/googledocs/docker-compose-hub.yml +61 -0
  125. dt_arena/envs/googledocs/docker-compose.yml +78 -0
  126. dt_arena/envs/hospital/docker-compose-hub.yml +25 -0
  127. dt_arena/envs/hospital/docker-compose.yml +27 -0
  128. dt_arena/envs/legal/docker-compose.yml +22 -0
  129. dt_arena/envs/linkedin/docker-compose.yml +63 -0
  130. dt_arena/envs/macos/docker-compose.yml +79 -0
  131. dt_arena/envs/os-filesystem/docker-compose-hub.yml +16 -0
  132. dt_arena/envs/os-filesystem/docker-compose.yml +20 -0
  133. dt_arena/envs/paypal/docker-compose-hub.yml +48 -0
  134. dt_arena/envs/paypal/docker-compose.yml +63 -0
  135. dt_arena/envs/research/docker-compose-hub.yml +13 -0
  136. dt_arena/envs/research/docker-compose.yml +24 -0
  137. dt_arena/envs/salesforce_crm/docker-compose-hub.yaml +45 -0
  138. dt_arena/envs/salesforce_crm/docker-compose.yaml +49 -0
  139. dt_arena/envs/slack/docker-compose-hub.yml +28 -0
  140. dt_arena/envs/slack/docker-compose.yml +41 -0
  141. dt_arena/envs/snowflake/docker-compose-hub.yml +41 -0
  142. dt_arena/envs/snowflake/docker-compose.yml +44 -0
  143. dt_arena/envs/telecom/docker-compose-hub.yml +16 -0
  144. dt_arena/envs/telecom/docker-compose.yml +17 -0
  145. dt_arena/envs/telegram/docker-compose-hub.yml +57 -0
  146. dt_arena/envs/telegram/docker-compose.yml +62 -0
  147. dt_arena/envs/terminal/docker-compose-hub.yml +12 -0
  148. dt_arena/envs/terminal/docker-compose.yml +26 -0
  149. dt_arena/envs/travel/docker-compose-hub.yml +19 -0
  150. dt_arena/envs/travel/docker-compose.yml +19 -0
  151. dt_arena/envs/whatsapp/docker-compose-hub.yml +61 -0
  152. dt_arena/envs/whatsapp/docker-compose.yml +78 -0
  153. dt_arena/envs/windows/docker-compose.yml +71 -0
  154. dt_arena/envs/zoom/docker-compose-hub.yml +27 -0
  155. dt_arena/envs/zoom/docker-compose.yml +40 -0
  156. dt_arena/injection_mcp_server/atlassian/env_injection.py +134 -0
  157. dt_arena/injection_mcp_server/calendar/env_injection.py +217 -0
  158. dt_arena/injection_mcp_server/custom_website/env_injection.py +97 -0
  159. dt_arena/injection_mcp_server/customer_service/env_injection.py +659 -0
  160. dt_arena/injection_mcp_server/databricks/env_injection.py +255 -0
  161. dt_arena/injection_mcp_server/ecommerce/env_injection.py +110 -0
  162. dt_arena/injection_mcp_server/finance/env_injection.py +85 -0
  163. dt_arena/injection_mcp_server/github/env_injection.py +206 -0
  164. dt_arena/injection_mcp_server/gmail/env_injection.py +211 -0
  165. dt_arena/injection_mcp_server/google_form/env_injection.py +186 -0
  166. dt_arena/injection_mcp_server/googledocs/env_injection.py +44 -0
  167. dt_arena/injection_mcp_server/hospital/env_injection.py +43 -0
  168. dt_arena/injection_mcp_server/legal/env_injection.py +229 -0
  169. dt_arena/injection_mcp_server/macos/env_injection.py +272 -0
  170. dt_arena/injection_mcp_server/os-filesystem/env_injection.py +341 -0
  171. dt_arena/injection_mcp_server/paypal/env_injection.py +268 -0
  172. dt_arena/injection_mcp_server/research/env_injection.py +616 -0
  173. dt_arena/injection_mcp_server/salesforce/env_injection.py +514 -0
  174. dt_arena/injection_mcp_server/slack/env_injection.py +265 -0
  175. dt_arena/injection_mcp_server/snowflake/env_injection.py +230 -0
  176. dt_arena/injection_mcp_server/telecom/env_injection.py +503 -0
  177. dt_arena/injection_mcp_server/telegram/env_injection.py +171 -0
  178. dt_arena/injection_mcp_server/terminal/env_injection.py +523 -0
  179. dt_arena/injection_mcp_server/travel/env_injection.py +173 -0
  180. dt_arena/injection_mcp_server/whatsapp/env_injection.py +185 -0
  181. dt_arena/injection_mcp_server/windows/env_injection.py +943 -0
  182. dt_arena/injection_mcp_server/zoom/env_injection.py +216 -0
  183. dt_arena/mcp_server/atlassian/main.py +1554 -0
  184. dt_arena/mcp_server/atlassian/test_server.py +66 -0
  185. dt_arena/mcp_server/bigquery/main.py +333 -0
  186. dt_arena/mcp_server/booking/main.py +310 -0
  187. dt_arena/mcp_server/browser/main.py +1741 -0
  188. dt_arena/mcp_server/calendar/example_multi_user.py +162 -0
  189. dt_arena/mcp_server/calendar/main.py +792 -0
  190. dt_arena/mcp_server/calendar/test_mcp.py +135 -0
  191. dt_arena/mcp_server/customer_service/main.py +1063 -0
  192. dt_arena/mcp_server/databricks/main.py +566 -0
  193. dt_arena/mcp_server/databricks/probe.py +102 -0
  194. dt_arena/mcp_server/ers/main.py +845 -0
  195. dt_arena/mcp_server/finance/__init__.py +87 -0
  196. dt_arena/mcp_server/finance/core/__init__.py +12 -0
  197. dt_arena/mcp_server/finance/core/data_loader.py +558 -0
  198. dt_arena/mcp_server/finance/core/portfolio.py +565 -0
  199. dt_arena/mcp_server/finance/evaluation/__init__.py +20 -0
  200. dt_arena/mcp_server/finance/evaluation/evaluator.py +217 -0
  201. dt_arena/mcp_server/finance/evaluation/logger.py +137 -0
  202. dt_arena/mcp_server/finance/injection/__init__.py +66 -0
  203. dt_arena/mcp_server/finance/injection/config.py +176 -0
  204. dt_arena/mcp_server/finance/injection/content.py +755 -0
  205. dt_arena/mcp_server/finance/injection/html.py +409 -0
  206. dt_arena/mcp_server/finance/injection/locations.py +167 -0
  207. dt_arena/mcp_server/finance/injection/methods.py +193 -0
  208. dt_arena/mcp_server/finance/injection/presets.py +1023 -0
  209. dt_arena/mcp_server/finance/main.py +361 -0
  210. dt_arena/mcp_server/finance/run_mcp.py +21 -0
  211. dt_arena/mcp_server/finance/run_web.py +26 -0
  212. dt_arena/mcp_server/finance/server/__init__.py +41 -0
  213. dt_arena/mcp_server/finance/server/extractor.py +1453 -0
  214. dt_arena/mcp_server/finance/server/extractor_minimal.py +292 -0
  215. dt_arena/mcp_server/finance/server/extractor_simple.py +1164 -0
  216. dt_arena/mcp_server/finance/server/injection_mcp.py +865 -0
  217. dt_arena/mcp_server/finance/server/mcp.py +451 -0
  218. dt_arena/mcp_server/finance/server/tools/__init__.py +23 -0
  219. dt_arena/mcp_server/finance/server/tools/account.py +88 -0
  220. dt_arena/mcp_server/finance/server/tools/browsing.py +328 -0
  221. dt_arena/mcp_server/finance/server/tools/social.py +73 -0
  222. dt_arena/mcp_server/finance/server/tools/trading.py +242 -0
  223. dt_arena/mcp_server/finance/server/tools/utility.py +49 -0
  224. dt_arena/mcp_server/finance/server/web.py +2139 -0
  225. dt_arena/mcp_server/finance/tasks/benchmark/__init__.py +28 -0
  226. dt_arena/mcp_server/finance/tasks/benchmark/attack_pool.py +3026 -0
  227. dt_arena/mcp_server/finance/tasks/benchmark/attack_runner.py +1315 -0
  228. dt_arena/mcp_server/finance/tasks/benchmark/finra_requirements.py +1335 -0
  229. dt_arena/mcp_server/finance/tasks/benchmark/finra_tasks.py +3665 -0
  230. dt_arena/mcp_server/finance/tasks/benchmark/malicious_tasks.py +2673 -0
  231. dt_arena/mcp_server/finance/tasks/redteam_suite/run_redteam_suite.py +1713 -0
  232. dt_arena/mcp_server/finance/test_mcp_tools.py +476 -0
  233. dt_arena/mcp_server/github/main.py +441 -0
  234. dt_arena/mcp_server/gmail/main.py +1004 -0
  235. dt_arena/mcp_server/google_form/main.py +141 -0
  236. dt_arena/mcp_server/googledocs/main.py +458 -0
  237. dt_arena/mcp_server/hospital/mcp_server.py +458 -0
  238. dt_arena/mcp_server/legal/__init__.py +9 -0
  239. dt_arena/mcp_server/legal/core/__init__.py +14 -0
  240. dt_arena/mcp_server/legal/core/courtlistener_store.py +762 -0
  241. dt_arena/mcp_server/legal/core/data_loader.py +266 -0
  242. dt_arena/mcp_server/legal/core/document_store.py +197 -0
  243. dt_arena/mcp_server/legal/core/matter_manager.py +466 -0
  244. dt_arena/mcp_server/legal/main.py +89 -0
  245. dt_arena/mcp_server/legal/scripts/collect_data.py +988 -0
  246. dt_arena/mcp_server/legal/server/__init__.py +14 -0
  247. dt_arena/mcp_server/legal/server/mcp.py +2330 -0
  248. dt_arena/mcp_server/macos/client_test.py +270 -0
  249. dt_arena/mcp_server/macos/mcp_server.py +285 -0
  250. dt_arena/mcp_server/os-filesystem/main.py +1380 -0
  251. dt_arena/mcp_server/paypal/main.py +501 -0
  252. dt_arena/mcp_server/research/main.py +777 -0
  253. dt_arena/mcp_server/salesforce/main.py +2006 -0
  254. dt_arena/mcp_server/slack/main.py +318 -0
  255. dt_arena/mcp_server/snowflake/main.py +612 -0
  256. dt_arena/mcp_server/snowflake/probe.py +183 -0
  257. dt_arena/mcp_server/telecom/mcp_client.py +423 -0
  258. dt_arena/mcp_server/telecom/mcp_server.py +1059 -0
  259. dt_arena/mcp_server/telegram/main.py +338 -0
  260. dt_arena/mcp_server/terminal/main.py +163 -0
  261. dt_arena/mcp_server/travel/client_test.py +16 -0
  262. dt_arena/mcp_server/travel/mcp_server.py +404 -0
  263. dt_arena/mcp_server/whatsapp/main.py +318 -0
  264. dt_arena/mcp_server/windows/client_test.py +270 -0
  265. dt_arena/mcp_server/windows/mcp_server.py +218 -0
  266. dt_arena/mcp_server/zoom/main.py +466 -0
  267. dt_arena/src/__init__.py +0 -0
  268. dt_arena/src/hooks/__init__.py +0 -0
  269. dt_arena/src/hooks/audit_log.py +30 -0
  270. dt_arena/src/hooks/hooks.json +3 -0
  271. dt_arena/src/run_benign.py +142 -0
  272. dt_arena/src/types/__init__.py +0 -0
  273. dt_arena/src/types/agent.py +441 -0
  274. dt_arena/src/types/attacks.py +2 -0
  275. dt_arena/src/types/environment.py +2 -0
  276. dt_arena/src/types/hooks.py +174 -0
  277. dt_arena/src/types/judge.py +52 -0
  278. dt_arena/src/types/red_teaming_trajectory.py +385 -0
  279. dt_arena/src/types/task.py +260 -0
  280. dt_arena/src/types/trajectory.py +315 -0
  281. dt_arena/utils/__init__.py +1 -0
  282. dt_arena/utils/atlassian/__init__.py +27 -0
  283. dt_arena/utils/atlassian/helpers.py +520 -0
  284. dt_arena/utils/bigquery/__init__.py +1 -0
  285. dt_arena/utils/bigquery/helpers.py +246 -0
  286. dt_arena/utils/calendar/__init__.py +1 -0
  287. dt_arena/utils/calendar/helpers.py +87 -0
  288. dt_arena/utils/customer_service/__init__.py +17 -0
  289. dt_arena/utils/customer_service/cs_env_client.py +940 -0
  290. dt_arena/utils/customer_service/helpers.py +339 -0
  291. dt_arena/utils/customer_service/judges/__init__.py +20 -0
  292. dt_arena/utils/customer_service/judges/abusive_comms.py +149 -0
  293. dt_arena/utils/customer_service/judges/bulk_destructive.py +202 -0
  294. dt_arena/utils/customer_service/judges/cross_customer.py +481 -0
  295. dt_arena/utils/customer_service/judges/email_exfiltration.py +320 -0
  296. dt_arena/utils/customer_service/judges/financial_fraud.py +221 -0
  297. dt_arena/utils/customer_service/judges/llm_utils.py +264 -0
  298. dt_arena/utils/customer_service/judges/pii_disclosure.py +837 -0
  299. dt_arena/utils/customer_service/judges/policy_misrep.py +44 -0
  300. dt_arena/utils/customer_service/judges/text_utils.py +21 -0
  301. dt_arena/utils/databricks/__init__.py +2 -0
  302. dt_arena/utils/databricks/helpers.py +210 -0
  303. dt_arena/utils/finance/__init__.py +0 -0
  304. dt_arena/utils/finance/helpers.py +263 -0
  305. dt_arena/utils/github/__init__.py +1 -0
  306. dt_arena/utils/github/helpers.py +249 -0
  307. dt_arena/utils/gmail/__init__.py +1 -0
  308. dt_arena/utils/gmail/helpers.py +344 -0
  309. dt_arena/utils/google_form/__init__.py +2 -0
  310. dt_arena/utils/google_form/helpers.py +133 -0
  311. dt_arena/utils/legal/__init__.py +0 -0
  312. dt_arena/utils/legal/helpers.py +228 -0
  313. dt_arena/utils/macos/__init__.py +0 -0
  314. dt_arena/utils/macos/env_setup.py +215 -0
  315. dt_arena/utils/macos/helpers.py +61 -0
  316. dt_arena/utils/os_filesystem/__init__.py +1 -0
  317. dt_arena/utils/os_filesystem/helpers.py +366 -0
  318. dt_arena/utils/paypal/__init__.py +1 -0
  319. dt_arena/utils/paypal/helpers.py +178 -0
  320. dt_arena/utils/port_allocator.py +266 -0
  321. dt_arena/utils/research/__init__.py +0 -0
  322. dt_arena/utils/research/helpers.py +251 -0
  323. dt_arena/utils/salesforce/__init__.py +1 -0
  324. dt_arena/utils/salesforce/helpers.py +719 -0
  325. dt_arena/utils/slack/__init__.py +1 -0
  326. dt_arena/utils/slack/helpers.py +176 -0
  327. dt_arena/utils/snowflake/__init__.py +1 -0
  328. dt_arena/utils/snowflake/helpers.py +166 -0
  329. dt_arena/utils/telecom/__init__.py +1 -0
  330. dt_arena/utils/telecom/helpers.py +760 -0
  331. dt_arena/utils/telegram/__init__.py +0 -0
  332. dt_arena/utils/telegram/helpers.py +174 -0
  333. dt_arena/utils/terminal/__init__.py +0 -0
  334. dt_arena/utils/terminal/helpers.py +20 -0
  335. dt_arena/utils/travel/__init__.py +0 -0
  336. dt_arena/utils/travel/env_client.py +537 -0
  337. dt_arena/utils/travel/llm_judge.py +137 -0
  338. dt_arena/utils/travel/prompts.py +64 -0
  339. dt_arena/utils/utils/__init__.py +122 -0
  340. dt_arena/utils/whatsapp/__init__.py +0 -0
  341. dt_arena/utils/whatsapp/helpers.py +226 -0
  342. dt_arena/utils/windows/__init__.py +0 -0
  343. dt_arena/utils/windows/env_reset.py +224 -0
  344. dt_arena/utils/windows/env_setup.py +280 -0
  345. dt_arena/utils/windows/exfil_helpers.py +170 -0
  346. dt_arena/utils/windows/helpers.py +74 -0
  347. dt_arena/utils/zoom/__init__.py +1 -0
  348. dt_arena/utils/zoom/helpers.py +70 -0
  349. eval/__init__.py +1 -0
  350. eval/evaluation.py +426 -0
  351. eval/task_runner.py +449 -0
  352. utils/__init__.py +148 -0
  353. utils/agent_helpers.py +308 -0
  354. utils/agent_wrapper.py +189 -0
  355. utils/compose_utils.py +135 -0
  356. utils/config.py +77 -0
  357. utils/env_helpers.py +104 -0
  358. utils/eval_stats.py +88 -0
  359. utils/injection_helpers.py +429 -0
  360. utils/injection_mcp_helpers.py +152 -0
  361. utils/judge_helpers.py +181 -0
  362. utils/judge_utils.py +472 -0
  363. utils/llm.py +196 -0
  364. utils/logging.py +45 -0
  365. utils/mcp_helpers.py +232 -0
  366. utils/mcp_manager.py +235 -0
  367. utils/memory_guard.py +18 -0
  368. utils/red_teaming_sandbox.py +476 -0
  369. utils/reset_helpers.py +318 -0
  370. utils/resource_manager.py +370 -0
  371. utils/skill_helpers.py +447 -0
  372. utils/task_executor.py +904 -0
  373. utils/task_helpers.py +270 -0
  374. utils/template_helpers.py +179 -0
@@ -0,0 +1,493 @@
1
+ import os
2
+ import json
3
+ import re
4
+ import threading
5
+ from datetime import datetime, timezone
6
+ from typing import List, Dict, Any, Optional
7
+ from collections import defaultdict
8
+
9
+ from agents.tracing import TracingProcessor
10
+
11
+ from dt_arena.src.types.trajectory import Trajectory
12
+
13
+
14
+ _HARMONY_FINAL_RE = re.compile(r"(?:^|.*)assistantfinal", re.DOTALL)
15
+
16
+
17
+ def extract_harmony_final_channel(text: str) -> str:
18
+ """Strip gpt-oss harmony channel markers and return only the user-facing final channel.
19
+
20
+ gpt-oss responses concatenate analysis (reasoning), commentary (tool calls), and
21
+ final (user reply) channels into a single string. The harmony format renders as
22
+ 'assistantfinal' (assistant role + final channel) before the message body. Some
23
+ outputs further include a literal 'final' label, producing 'assistantfinalfinal'.
24
+
25
+ Strategy: locate the last 'assistantfinal', drop it, then drop any immediately
26
+ following 'final' tokens (they are residual channel labels, not content).
27
+ """
28
+ if not text or not isinstance(text, str):
29
+ return text
30
+ if "assistantfinal" not in text:
31
+ return text
32
+ idx = text.rfind("assistantfinal")
33
+ rest = text[idx + len("assistantfinal"):].lstrip()
34
+ # Drop residual "final" channel-label (e.g. 'assistantfinalfinal**Hi**' → '**Hi**').
35
+ # Only strip when 'final' is immediately followed by a markdown/structural marker
36
+ # (*, #, |, \n) — ensures we never strip legitimate content like "final answer:".
37
+ if rest.startswith("final") and len(rest) > 5 and rest[5] in "*#|\n":
38
+ rest = rest[5:].lstrip()
39
+ return rest
40
+
41
+
42
+
43
+ # OpenAI Local Trace Processor
44
+ class OpenAITraceProcessor(TracingProcessor):
45
+ """
46
+ Local agent trace processor that captures all span data and agent final responses.
47
+ """
48
+
49
+ def __init__(self, path):
50
+ self.path = path
51
+ self._lock = threading.Lock()
52
+ self._span_start_times: Dict[str, float] = {} # span_id -> monotonic start
53
+
54
+ def _iso(self, ts):
55
+ try:
56
+ return ts.isoformat()
57
+ except:
58
+ return None
59
+
60
+ def _safe_export_span_data(self, sd):
61
+ """Export span data preserving all fields"""
62
+ if sd is None:
63
+ return None
64
+
65
+ data = {}
66
+ try:
67
+ if hasattr(sd, "export") and callable(sd.export):
68
+ exported = sd.export()
69
+ if isinstance(exported, dict):
70
+ data = exported
71
+ except:
72
+ pass
73
+
74
+ if "type" not in data:
75
+ data["type"] = getattr(sd, "type", sd.__class__.__name__)
76
+
77
+ for field in ["input", "name", "output", "mcp_data", "response_id", "tools", "handoffs"]:
78
+ if hasattr(sd, field) and field not in data:
79
+ value = getattr(sd, field, None)
80
+ if value is not None:
81
+ data[field] = value
82
+
83
+ return data
84
+
85
+ def _write(self, obj):
86
+ with self._lock:
87
+ try:
88
+ with open(self.path, "a", encoding="utf-8") as f:
89
+ f.write(json.dumps(obj, ensure_ascii=False, default=str) + "\n")
90
+ except Exception as e:
91
+ pass
92
+
93
+ def write_agent_response(self, trace_id: str, response_text: str):
94
+ """Record agent's final response (harmony channels stripped to final-only)."""
95
+ cleaned = extract_harmony_final_channel(response_text)
96
+ self._write({
97
+ "record": "response_final",
98
+ "trace_id": trace_id,
99
+ "response_text": cleaned,
100
+ "ts": datetime.now(timezone.utc).isoformat(),
101
+ })
102
+
103
+ def on_span_end(self, span):
104
+ import time as _time
105
+
106
+ span_id = getattr(span, "span_id", None)
107
+ duration_s = None
108
+ start_t = self._span_start_times.pop(span_id, None) if span_id else None
109
+ if start_t is not None:
110
+ duration_s = round(_time.monotonic() - start_t, 3)
111
+
112
+ span_data = self._safe_export_span_data(getattr(span, "span_data", None))
113
+
114
+ # Log MCP tool call duration to stderr for visibility
115
+ if span_data and span_data.get("type") == "function" and duration_s is not None:
116
+ tool_name = span_data.get("name", "?")
117
+ mcp_data = span_data.get("mcp_data") or {}
118
+ server = mcp_data.get("server", "?")
119
+ import sys
120
+ print(f"[MCP] {server}/{tool_name} took {duration_s:.3f}s", file=sys.stderr)
121
+
122
+ self._write({
123
+ "record": "span_end",
124
+ "trace_id": getattr(span, "trace_id", None),
125
+ "span_id": getattr(span, "span_id", None),
126
+ "parent_id": getattr(span, "parent_id", None),
127
+ "started_at": self._iso(getattr(span, "started_at", None)),
128
+ "ended_at": self._iso(getattr(span, "ended_at", None)),
129
+ "duration_s": duration_s,
130
+ "span_data": span_data,
131
+ "ts": datetime.now(timezone.utc).isoformat(),
132
+ })
133
+
134
+ def on_trace_end(self, trace):
135
+ self._write({
136
+ "record": "trace_end",
137
+ "trace_id": getattr(trace, "trace_id", None),
138
+ "workflow_name": getattr(trace, "workflow_name", None),
139
+ "group_id": getattr(trace, "group_id", None),
140
+ "metadata": getattr(trace, "metadata", {}) or {},
141
+ "started_at": self._iso(getattr(trace, "started_at", None)),
142
+ "ended_at": self._iso(getattr(trace, "ended_at", None)),
143
+ "ts": datetime.now(timezone.utc).isoformat(),
144
+ })
145
+
146
+ def on_trace_start(self, trace): pass
147
+
148
+ def on_span_start(self, span):
149
+ import time
150
+ span_id = getattr(span, "span_id", None)
151
+ if span_id:
152
+ self._span_start_times[span_id] = time.monotonic()
153
+ def force_flush(self): pass
154
+ def shutdown(self): pass
155
+
156
+
157
+ # ============================================================================
158
+ # Standard Trajectory Converter
159
+ # ============================================================================
160
+
161
+ class OpenAIAgentTrajectory:
162
+ """Convert OpenAI agent traces to standard trajectory format"""
163
+
164
+ def __init__(self, output_dir: str, timestamp: Optional[str] = None):
165
+ self.output_dir = output_dir
166
+ self.timestamp = timestamp # Used for output filename
167
+ os.makedirs(self.output_dir, exist_ok=True)
168
+
169
+ def load_traces(self, trace_file: str):
170
+ if not os.path.exists(trace_file):
171
+ raise FileNotFoundError(f"Trace file not found: {trace_file}")
172
+
173
+ traces_by_id = defaultdict(list)
174
+ trace_metadata = defaultdict(list)
175
+ agent_final_response = defaultdict(list)
176
+
177
+ with open(trace_file, 'r', encoding='utf-8') as f:
178
+ for line in f:
179
+ if not line.strip():
180
+ continue
181
+ data = json.loads(line)
182
+
183
+ if data.get("record") == "trace_end":
184
+ trace_id = data["trace_id"]
185
+ trace_metadata[trace_id].append(data)
186
+
187
+ elif data.get("record") == "span_end":
188
+ trace_id = data["trace_id"]
189
+ traces_by_id[trace_id].append(data)
190
+
191
+ elif data.get("record") == "response_final":
192
+ trace_id = data["trace_id"]
193
+ agent_final_response[trace_id].append(
194
+ extract_harmony_final_channel(data.get("response_text", ""))
195
+ )
196
+
197
+
198
+ return traces_by_id, trace_metadata, agent_final_response
199
+
200
+ def _parse_tool_input(self, input_str: str) -> Dict[str, Any]:
201
+ try:
202
+ return json.loads(input_str) if isinstance(input_str, str) else input_str
203
+ except:
204
+ return {"raw": input_str}
205
+
206
+ def _parse_tool_output(self, output_str: str) -> Any:
207
+ try:
208
+ if isinstance(output_str, str):
209
+ parsed = json.loads(output_str)
210
+ if isinstance(parsed, dict) and "type" in parsed and parsed["type"] == "text":
211
+ text_content = parsed.get("text", "")
212
+ try:
213
+ return json.loads(text_content)
214
+ except:
215
+ return text_content
216
+ return parsed
217
+ return output_str
218
+ except:
219
+ return output_str
220
+
221
+ def _extract_conversation_messages(self, response_span: Dict) -> List[Dict[str, Any]]:
222
+ """
223
+ Extract all conversation messages from response span in order.
224
+ Returns list of {"role": "user"|"assistant", "content": str} dicts.
225
+ """
226
+ messages = []
227
+ span_data = response_span.get("span_data", {})
228
+ input_data = span_data.get("input")
229
+
230
+ if isinstance(input_data, list):
231
+ for msg in input_data:
232
+ if isinstance(msg, dict):
233
+ role = msg.get("role")
234
+
235
+ if role == "user":
236
+ content = msg.get("content")
237
+ if content:
238
+ messages.append({"role": "user", "content": content})
239
+
240
+ elif role == "assistant":
241
+ # Assistant messages have content as a list of blocks
242
+ content = msg.get("content", [])
243
+ if isinstance(content, list):
244
+ for block in content:
245
+ if isinstance(block, dict):
246
+ # Look for text/output_text blocks
247
+ text = block.get("text") or block.get("output_text")
248
+ if text:
249
+ messages.append({"role": "assistant", "content": text})
250
+ elif isinstance(content, str) and content:
251
+ messages.append({"role": "assistant", "content": content})
252
+
253
+ return messages
254
+
255
+ def _extract_agent_final_response_from_spans(self, spans: List[Dict]) -> Optional[str]:
256
+ """Fallback: extract agent's final response from spans"""
257
+ sorted_spans = sorted(spans, key=lambda x: x.get("ts", ""), reverse=True)
258
+
259
+ for span in sorted_spans:
260
+ span_data = span.get("span_data", {})
261
+ if span_data.get("type") == "response":
262
+ output = span_data.get("output")
263
+ if output:
264
+ if isinstance(output, dict):
265
+ if "content" in output:
266
+ return output["content"]
267
+ elif "text" in output:
268
+ return output["text"]
269
+ elif isinstance(output, str):
270
+ return output
271
+
272
+ input_data = span_data.get("input", [])
273
+ if isinstance(input_data, list):
274
+ for msg in reversed(input_data):
275
+ if isinstance(msg, dict) and msg.get("role") in ("assistant", "agent"):
276
+ content = msg.get("content")
277
+ if content:
278
+ return content
279
+
280
+ return None
281
+
282
+ def _build_trajectory_steps(
283
+ self,
284
+ traj: Trajectory,
285
+ spans: List[Dict],
286
+ agent_response: Optional[str] = None
287
+ ):
288
+ """Build trajectory steps from spans using Trajectory class helper methods"""
289
+ sorted_spans = sorted(spans, key=lambda x: x.get("ts", ""))
290
+
291
+ seen_user_messages = set()
292
+ seen_assistant_messages = set()
293
+
294
+ for span in sorted_spans:
295
+ span_data = span.get("span_data", {})
296
+ span_type = span_data.get("type")
297
+
298
+ # List MCP Tools action (native tool)
299
+ if span_type == "mcp_tools":
300
+ tools_list = span_data.get("result", [])
301
+ server_name = span_data.get("server", "unknown")
302
+
303
+ # Agent action
304
+ traj.append_agent_step(
305
+ action=f"List MCP Tools ({server_name})",
306
+ tool_name="List MCP Tools",
307
+ tool_params={},
308
+ server=server_name
309
+ )
310
+
311
+ # Tool return
312
+ traj.append_tool_return(
313
+ result=tools_list,
314
+ tool_name="List MCP Tools",
315
+ server=server_name
316
+ )
317
+
318
+ if span_type == "response":
319
+ # Extract conversation messages in order (user and assistant interleaved)
320
+ conv_messages = self._extract_conversation_messages(span)
321
+ for msg in conv_messages:
322
+ if msg["role"] == "user":
323
+ if msg["content"] not in seen_user_messages:
324
+ traj.append_user_step(msg["content"])
325
+ seen_user_messages.add(msg["content"])
326
+ elif msg["role"] == "assistant":
327
+ if msg["content"] not in seen_assistant_messages:
328
+ traj.append_agent_step(
329
+ action="send_message_to_user",
330
+ metadata={"message": msg["content"]}
331
+ )
332
+ seen_assistant_messages.add(msg["content"])
333
+ # Set the latest response in trajectory
334
+ traj.set_final_response(msg["content"])
335
+
336
+ elif span_type == "function":
337
+ tool_name = span_data.get("name")
338
+ tool_input = span_data.get("input", "{}")
339
+ tool_output = span_data.get("output", "")
340
+ mcp_data = span_data.get("mcp_data", {})
341
+
342
+ parsed_input = self._parse_tool_input(tool_input)
343
+ parsed_output = self._parse_tool_output(tool_output)
344
+
345
+ # Get server name from mcp_data (authoritative source for which server handled the call)
346
+ server_name = mcp_data.get("server", "unknown") if mcp_data else "unknown"
347
+
348
+ # Compute tool call duration from span timestamps
349
+ duration_s = span.get("duration_s")
350
+
351
+ # Build action string
352
+ action_str = f"{tool_name}("
353
+ params = []
354
+ for k, v in parsed_input.items():
355
+ if isinstance(v, str):
356
+ params.append(f'{k}="{v}"')
357
+ else:
358
+ params.append(f'{k}={v}')
359
+ action_str += ", ".join(params) + ")"
360
+
361
+ # Add agent action
362
+ traj.append_agent_step(
363
+ action=action_str,
364
+ tool_name=tool_name,
365
+ tool_params=parsed_input,
366
+ server=server_name
367
+ )
368
+
369
+ # Add tool return with duration metadata
370
+ tool_meta = {}
371
+ if duration_s is not None:
372
+ tool_meta["duration_s"] = duration_s
373
+ traj.append_tool_return(
374
+ result=parsed_output,
375
+ tool_name=tool_name,
376
+ server=server_name,
377
+ metadata=tool_meta if tool_meta else None,
378
+ )
379
+
380
+ # Add agent final response (only if not already seen)
381
+ final_response_text = agent_response or self._extract_agent_final_response_from_spans(spans)
382
+
383
+ if final_response_text and final_response_text not in seen_assistant_messages:
384
+ traj.append_agent_step(
385
+ action="send_message_to_user",
386
+ metadata={"message": final_response_text}
387
+ )
388
+ # Set final response
389
+ traj.set_final_response(final_response_text)
390
+
391
+ def convert_trace_to_trajectory(
392
+ self,
393
+ trace_id: str,
394
+ spans: List[Dict],
395
+ metadata: Dict,
396
+ task_id: Optional[str] = None,
397
+ agent_response: Optional[str] = None
398
+ ) -> Trajectory:
399
+ """Convert a single trace to Trajectory object"""
400
+ trace_metadata = metadata.get("metadata", {})
401
+ instruction = trace_metadata.get("instruction", "No instruction provided")
402
+ malicious_instruction = trace_metadata.get("malicious_goal", None)
403
+
404
+ # Create Trajectory instance
405
+ traj = Trajectory(
406
+ task_id=task_id or trace_metadata.get("task_id", trace_id[:16]),
407
+ original_instruction=instruction,
408
+ malicious_instruction=malicious_instruction,
409
+ domain=trace_metadata.get("domain"),
410
+ risk_category=trace_metadata.get("category")
411
+ )
412
+
413
+ # Calculate duration from spans
414
+ if spans:
415
+ timestamps = [s.get("ts") for s in spans if s.get("ts")]
416
+ if len(timestamps) >= 2:
417
+ try:
418
+ start = datetime.fromisoformat(timestamps[0].replace('Z', '+00:00'))
419
+ end = datetime.fromisoformat(timestamps[-1].replace('Z', '+00:00'))
420
+ duration = (end - start).total_seconds()
421
+ except:
422
+ duration = 0.0
423
+ else:
424
+ duration = 0.0
425
+ else:
426
+ duration = 0.0
427
+
428
+ # Set duration and timestamp
429
+ traj.data["traj_info"]["duration"] = round(duration, 3)
430
+
431
+ # Priority: trace ended_at > trace started_at > last span ts > now
432
+ trace_timestamp = metadata.get("ended_at") or metadata.get("started_at")
433
+ if trace_timestamp:
434
+ traj.data["traj_info"]["timestamp"] = trace_timestamp.isoformat() if hasattr(trace_timestamp, 'isoformat') else str(trace_timestamp)
435
+ elif timestamps:
436
+ traj.data["traj_info"]["timestamp"] = timestamps[-1]
437
+
438
+ traj.data["traj_info"]["metadata"] = {
439
+ "trace_id": trace_id
440
+ }
441
+
442
+ # Build trajectory steps using helper methods
443
+ self._build_trajectory_steps(traj, spans, agent_response)
444
+
445
+ # Include debug fields if present in trace metadata
446
+ debug_data = {}
447
+ tool_descriptions = trace_metadata.get("tool_descriptions")
448
+ if tool_descriptions:
449
+ debug_data["tool_descriptions"] = tool_descriptions
450
+ system_prompt = trace_metadata.get("system_prompt")
451
+ if system_prompt:
452
+ debug_data["system_prompt"] = system_prompt
453
+ if debug_data:
454
+ traj.data["debug"] = debug_data
455
+
456
+ return traj
457
+
458
+ def process_trace_file(self, trace_file: str, output_name: Optional[str] = None) -> List[Trajectory]:
459
+ """Process trace file and generate trajectory files
460
+
461
+ Returns:
462
+ List of Trajectory objects (also saved to files)
463
+ """
464
+ traces_by_id, trace_metadata, agent_final_response = self.load_traces(trace_file)
465
+
466
+ trajectories = []
467
+
468
+ for i, (trace_id, spans) in enumerate(traces_by_id.items()):
469
+ metadata = trace_metadata.get(trace_id)[-1] if trace_metadata.get(trace_id) else {}
470
+ task_id = metadata.get("metadata", {}).get("task_id")
471
+ agent_response = agent_final_response.get(trace_id)[-1] if agent_final_response.get(trace_id) else None
472
+
473
+ traj = self.convert_trace_to_trajectory(
474
+ trace_id, spans, metadata, task_id, agent_response
475
+ )
476
+
477
+ # Use timestamp for filename: {timestamp}.json or {timestamp}_{index}.json for multiple traces
478
+ if self.timestamp:
479
+ if len(traces_by_id) == 1:
480
+ filename = f"{self.timestamp}.json"
481
+ else:
482
+ filename = f"{self.timestamp}_{i}.json"
483
+ else:
484
+ filename = f"{task_id}_{trace_id}.json"
485
+ output_path = os.path.join(self.output_dir, filename)
486
+
487
+ with open(output_path, 'w', encoding='utf-8') as f:
488
+ json.dump(traj.to_dict(), f, indent=2, ensure_ascii=False)
489
+
490
+ trajectories.append(traj)
491
+
492
+ return trajectories
493
+
@@ -0,0 +1,10 @@
1
+ from .src.agent import OpenClawAgent
2
+ from .src.mcp_proxy import MCPProxyServer, MCPProxyManager
3
+ from .src.utils import OpenClawTrajectoryConverter
4
+
5
+ __all__ = [
6
+ "OpenClawAgent",
7
+ "MCPProxyServer",
8
+ "MCPProxyManager",
9
+ "OpenClawTrajectoryConverter",
10
+ ]