calvyn-code 0.14.0

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 (1718) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -0
  3. package/README.zh-CN.md +180 -0
  4. package/acp_adapter/__init__.py +1 -0
  5. package/acp_adapter/__main__.py +5 -0
  6. package/acp_adapter/auth.py +68 -0
  7. package/acp_adapter/bootstrap/__init__.py +0 -0
  8. package/acp_adapter/bootstrap/bootstrap_browser_tools.ps1 +288 -0
  9. package/acp_adapter/bootstrap/bootstrap_browser_tools.sh +399 -0
  10. package/acp_adapter/entry.py +292 -0
  11. package/acp_adapter/events.py +265 -0
  12. package/acp_adapter/permissions.py +148 -0
  13. package/acp_adapter/server.py +1713 -0
  14. package/acp_adapter/session.py +629 -0
  15. package/acp_adapter/tools.py +1180 -0
  16. package/agent/__init__.py +6 -0
  17. package/agent/__pycache__/__init__.cpython-312.pyc +0 -0
  18. package/agent/__pycache__/account_usage.cpython-312.pyc +0 -0
  19. package/agent/__pycache__/anthropic_adapter.cpython-312.pyc +0 -0
  20. package/agent/__pycache__/async_utils.cpython-312.pyc +0 -0
  21. package/agent/__pycache__/auxiliary_client.cpython-312.pyc +0 -0
  22. package/agent/__pycache__/codex_responses_adapter.cpython-312.pyc +0 -0
  23. package/agent/__pycache__/context_compressor.cpython-312.pyc +0 -0
  24. package/agent/__pycache__/context_engine.cpython-312.pyc +0 -0
  25. package/agent/__pycache__/context_references.cpython-312.pyc +0 -0
  26. package/agent/__pycache__/credential_pool.cpython-312.pyc +0 -0
  27. package/agent/__pycache__/curator.cpython-312.pyc +0 -0
  28. package/agent/__pycache__/display.cpython-312.pyc +0 -0
  29. package/agent/__pycache__/error_classifier.cpython-312.pyc +0 -0
  30. package/agent/__pycache__/file_safety.cpython-312.pyc +0 -0
  31. package/agent/__pycache__/google_code_assist.cpython-312.pyc +0 -0
  32. package/agent/__pycache__/google_oauth.cpython-312.pyc +0 -0
  33. package/agent/__pycache__/i18n.cpython-312.pyc +0 -0
  34. package/agent/__pycache__/image_gen_provider.cpython-312.pyc +0 -0
  35. package/agent/__pycache__/image_gen_registry.cpython-312.pyc +0 -0
  36. package/agent/__pycache__/insights.cpython-312.pyc +0 -0
  37. package/agent/__pycache__/lmstudio_reasoning.cpython-312.pyc +0 -0
  38. package/agent/__pycache__/manual_compression_feedback.cpython-312.pyc +0 -0
  39. package/agent/__pycache__/markdown_tables.cpython-312.pyc +0 -0
  40. package/agent/__pycache__/memory_manager.cpython-312.pyc +0 -0
  41. package/agent/__pycache__/memory_provider.cpython-312.pyc +0 -0
  42. package/agent/__pycache__/model_metadata.cpython-312.pyc +0 -0
  43. package/agent/__pycache__/models_dev.cpython-312.pyc +0 -0
  44. package/agent/__pycache__/moonshot_schema.cpython-312.pyc +0 -0
  45. package/agent/__pycache__/onboarding.cpython-312.pyc +0 -0
  46. package/agent/__pycache__/portal_tags.cpython-312.pyc +0 -0
  47. package/agent/__pycache__/prompt_builder.cpython-312.pyc +0 -0
  48. package/agent/__pycache__/prompt_caching.cpython-312.pyc +0 -0
  49. package/agent/__pycache__/redact.cpython-312.pyc +0 -0
  50. package/agent/__pycache__/retry_utils.cpython-312.pyc +0 -0
  51. package/agent/__pycache__/shell_hooks.cpython-312.pyc +0 -0
  52. package/agent/__pycache__/skill_commands.cpython-312.pyc +0 -0
  53. package/agent/__pycache__/skill_preprocessing.cpython-312.pyc +0 -0
  54. package/agent/__pycache__/skill_utils.cpython-312.pyc +0 -0
  55. package/agent/__pycache__/subdirectory_hints.cpython-312.pyc +0 -0
  56. package/agent/__pycache__/think_scrubber.cpython-312.pyc +0 -0
  57. package/agent/__pycache__/title_generator.cpython-312.pyc +0 -0
  58. package/agent/__pycache__/tool_guardrails.cpython-312.pyc +0 -0
  59. package/agent/__pycache__/tool_result_classification.cpython-312.pyc +0 -0
  60. package/agent/__pycache__/trajectory.cpython-312.pyc +0 -0
  61. package/agent/__pycache__/usage_pricing.cpython-312.pyc +0 -0
  62. package/agent/__pycache__/video_gen_provider.cpython-312.pyc +0 -0
  63. package/agent/__pycache__/video_gen_registry.cpython-312.pyc +0 -0
  64. package/agent/__pycache__/web_search_provider.cpython-312.pyc +0 -0
  65. package/agent/__pycache__/web_search_registry.cpython-312.pyc +0 -0
  66. package/agent/account_usage.py +326 -0
  67. package/agent/anthropic_adapter.py +2087 -0
  68. package/agent/async_utils.py +68 -0
  69. package/agent/auxiliary_client.py +4893 -0
  70. package/agent/bedrock_adapter.py +1276 -0
  71. package/agent/codex_responses_adapter.py +1084 -0
  72. package/agent/context_compressor.py +1583 -0
  73. package/agent/context_engine.py +211 -0
  74. package/agent/context_references.py +519 -0
  75. package/agent/copilot_acp_client.py +684 -0
  76. package/agent/credential_pool.py +1780 -0
  77. package/agent/credential_sources.py +449 -0
  78. package/agent/curator.py +1782 -0
  79. package/agent/curator_backup.py +694 -0
  80. package/agent/display.py +987 -0
  81. package/agent/error_classifier.py +1058 -0
  82. package/agent/file_safety.py +112 -0
  83. package/agent/gemini_cloudcode_adapter.py +909 -0
  84. package/agent/gemini_native_adapter.py +971 -0
  85. package/agent/gemini_schema.py +99 -0
  86. package/agent/google_code_assist.py +452 -0
  87. package/agent/google_oauth.py +1062 -0
  88. package/agent/i18n.py +258 -0
  89. package/agent/image_gen_provider.py +243 -0
  90. package/agent/image_gen_registry.py +145 -0
  91. package/agent/image_routing.py +301 -0
  92. package/agent/insights.py +931 -0
  93. package/agent/lmstudio_reasoning.py +48 -0
  94. package/agent/lsp/__init__.py +106 -0
  95. package/agent/lsp/__pycache__/__init__.cpython-312.pyc +0 -0
  96. package/agent/lsp/__pycache__/cli.cpython-312.pyc +0 -0
  97. package/agent/lsp/__pycache__/client.cpython-312.pyc +0 -0
  98. package/agent/lsp/__pycache__/eventlog.cpython-312.pyc +0 -0
  99. package/agent/lsp/__pycache__/manager.cpython-312.pyc +0 -0
  100. package/agent/lsp/__pycache__/protocol.cpython-312.pyc +0 -0
  101. package/agent/lsp/__pycache__/servers.cpython-312.pyc +0 -0
  102. package/agent/lsp/__pycache__/workspace.cpython-312.pyc +0 -0
  103. package/agent/lsp/cli.py +308 -0
  104. package/agent/lsp/client.py +930 -0
  105. package/agent/lsp/eventlog.py +213 -0
  106. package/agent/lsp/install.py +376 -0
  107. package/agent/lsp/manager.py +644 -0
  108. package/agent/lsp/protocol.py +196 -0
  109. package/agent/lsp/range_shift.py +149 -0
  110. package/agent/lsp/reporter.py +78 -0
  111. package/agent/lsp/servers.py +1040 -0
  112. package/agent/lsp/workspace.py +223 -0
  113. package/agent/manual_compression_feedback.py +49 -0
  114. package/agent/markdown_tables.py +309 -0
  115. package/agent/memory_manager.py +556 -0
  116. package/agent/memory_provider.py +279 -0
  117. package/agent/model_metadata.py +1827 -0
  118. package/agent/models_dev.py +724 -0
  119. package/agent/moonshot_schema.py +231 -0
  120. package/agent/nous_rate_guard.py +326 -0
  121. package/agent/onboarding.py +193 -0
  122. package/agent/plugin_llm.py +1046 -0
  123. package/agent/portal_tags.py +64 -0
  124. package/agent/prompt_builder.py +1457 -0
  125. package/agent/prompt_caching.py +79 -0
  126. package/agent/rate_limit_tracker.py +246 -0
  127. package/agent/redact.py +403 -0
  128. package/agent/retry_utils.py +57 -0
  129. package/agent/shell_hooks.py +837 -0
  130. package/agent/skill_commands.py +502 -0
  131. package/agent/skill_preprocessing.py +131 -0
  132. package/agent/skill_utils.py +512 -0
  133. package/agent/subdirectory_hints.py +224 -0
  134. package/agent/think_scrubber.py +386 -0
  135. package/agent/title_generator.py +171 -0
  136. package/agent/tool_guardrails.py +458 -0
  137. package/agent/tool_result_classification.py +26 -0
  138. package/agent/trajectory.py +56 -0
  139. package/agent/transports/__init__.py +68 -0
  140. package/agent/transports/__pycache__/__init__.cpython-312.pyc +0 -0
  141. package/agent/transports/__pycache__/anthropic.cpython-312.pyc +0 -0
  142. package/agent/transports/__pycache__/base.cpython-312.pyc +0 -0
  143. package/agent/transports/__pycache__/bedrock.cpython-312.pyc +0 -0
  144. package/agent/transports/__pycache__/chat_completions.cpython-312.pyc +0 -0
  145. package/agent/transports/__pycache__/codex.cpython-312.pyc +0 -0
  146. package/agent/transports/__pycache__/types.cpython-312.pyc +0 -0
  147. package/agent/transports/anthropic.py +179 -0
  148. package/agent/transports/base.py +89 -0
  149. package/agent/transports/bedrock.py +154 -0
  150. package/agent/transports/chat_completions.py +614 -0
  151. package/agent/transports/codex.py +283 -0
  152. package/agent/transports/codex_app_server.py +368 -0
  153. package/agent/transports/codex_app_server_session.py +810 -0
  154. package/agent/transports/codex_event_projector.py +312 -0
  155. package/agent/transports/hermes_tools_mcp_server.py +233 -0
  156. package/agent/transports/types.py +162 -0
  157. package/agent/usage_pricing.py +877 -0
  158. package/agent/video_gen_provider.py +300 -0
  159. package/agent/video_gen_registry.py +117 -0
  160. package/agent/web_search_provider.py +221 -0
  161. package/agent/web_search_registry.py +262 -0
  162. package/assets/banner.png +0 -0
  163. package/batch_runner.py +1303 -0
  164. package/bin/calvyn.js +67 -0
  165. package/calvyn_bootstrap.py +130 -0
  166. package/calvyn_constants.py +346 -0
  167. package/calvyn_logging.py +390 -0
  168. package/calvyn_state.py +2967 -0
  169. package/calvyn_time.py +105 -0
  170. package/cli.py +14160 -0
  171. package/cron/__init__.py +42 -0
  172. package/cron/__pycache__/__init__.cpython-312.pyc +0 -0
  173. package/cron/__pycache__/jobs.cpython-312.pyc +0 -0
  174. package/cron/__pycache__/scheduler.cpython-312.pyc +0 -0
  175. package/cron/jobs.py +1160 -0
  176. package/cron/scheduler.py +1832 -0
  177. package/gateway/__init__.py +35 -0
  178. package/gateway/__pycache__/__init__.cpython-312.pyc +0 -0
  179. package/gateway/__pycache__/channel_directory.cpython-312.pyc +0 -0
  180. package/gateway/__pycache__/config.cpython-312.pyc +0 -0
  181. package/gateway/__pycache__/delivery.cpython-312.pyc +0 -0
  182. package/gateway/__pycache__/display_config.cpython-312.pyc +0 -0
  183. package/gateway/__pycache__/hooks.cpython-312.pyc +0 -0
  184. package/gateway/__pycache__/pairing.cpython-312.pyc +0 -0
  185. package/gateway/__pycache__/platform_registry.cpython-312.pyc +0 -0
  186. package/gateway/__pycache__/restart.cpython-312.pyc +0 -0
  187. package/gateway/__pycache__/run.cpython-312.pyc +0 -0
  188. package/gateway/__pycache__/runtime_footer.cpython-312.pyc +0 -0
  189. package/gateway/__pycache__/session.cpython-312.pyc +0 -0
  190. package/gateway/__pycache__/session_context.cpython-312.pyc +0 -0
  191. package/gateway/__pycache__/shutdown_forensics.cpython-312.pyc +0 -0
  192. package/gateway/__pycache__/slash_access.cpython-312.pyc +0 -0
  193. package/gateway/__pycache__/status.cpython-312.pyc +0 -0
  194. package/gateway/__pycache__/stream_consumer.cpython-312.pyc +0 -0
  195. package/gateway/__pycache__/whatsapp_identity.cpython-312.pyc +0 -0
  196. package/gateway/assets/telegram-botfather-threads-settings.jpg +0 -0
  197. package/gateway/builtin_hooks/__init__.py +1 -0
  198. package/gateway/channel_directory.py +357 -0
  199. package/gateway/config.py +1873 -0
  200. package/gateway/delivery.py +258 -0
  201. package/gateway/display_config.py +206 -0
  202. package/gateway/hooks.py +210 -0
  203. package/gateway/mirror.py +179 -0
  204. package/gateway/pairing.py +322 -0
  205. package/gateway/platform_registry.py +260 -0
  206. package/gateway/platforms/ADDING_A_PLATFORM.md +374 -0
  207. package/gateway/platforms/__init__.py +45 -0
  208. package/gateway/platforms/__pycache__/__init__.cpython-312.pyc +0 -0
  209. package/gateway/platforms/__pycache__/base.cpython-312.pyc +0 -0
  210. package/gateway/platforms/__pycache__/helpers.cpython-312.pyc +0 -0
  211. package/gateway/platforms/__pycache__/telegram.cpython-312.pyc +0 -0
  212. package/gateway/platforms/__pycache__/telegram_network.cpython-312.pyc +0 -0
  213. package/gateway/platforms/__pycache__/yuanbao.cpython-312.pyc +0 -0
  214. package/gateway/platforms/__pycache__/yuanbao_media.cpython-312.pyc +0 -0
  215. package/gateway/platforms/__pycache__/yuanbao_proto.cpython-312.pyc +0 -0
  216. package/gateway/platforms/_http_client_limits.py +84 -0
  217. package/gateway/platforms/api_server.py +3488 -0
  218. package/gateway/platforms/base.py +3747 -0
  219. package/gateway/platforms/bluebubbles.py +937 -0
  220. package/gateway/platforms/dingtalk.py +1473 -0
  221. package/gateway/platforms/discord.py +5584 -0
  222. package/gateway/platforms/email.py +773 -0
  223. package/gateway/platforms/feishu.py +5059 -0
  224. package/gateway/platforms/feishu_comment.py +1382 -0
  225. package/gateway/platforms/feishu_comment_rules.py +430 -0
  226. package/gateway/platforms/helpers.py +279 -0
  227. package/gateway/platforms/homeassistant.py +449 -0
  228. package/gateway/platforms/matrix.py +2777 -0
  229. package/gateway/platforms/mattermost.py +852 -0
  230. package/gateway/platforms/msgraph_webhook.py +397 -0
  231. package/gateway/platforms/qqbot/__init__.py +91 -0
  232. package/gateway/platforms/qqbot/adapter.py +3072 -0
  233. package/gateway/platforms/qqbot/chunked_upload.py +602 -0
  234. package/gateway/platforms/qqbot/constants.py +74 -0
  235. package/gateway/platforms/qqbot/crypto.py +45 -0
  236. package/gateway/platforms/qqbot/keyboards.py +473 -0
  237. package/gateway/platforms/qqbot/onboard.py +220 -0
  238. package/gateway/platforms/qqbot/utils.py +71 -0
  239. package/gateway/platforms/signal.py +1518 -0
  240. package/gateway/platforms/signal_rate_limit.py +369 -0
  241. package/gateway/platforms/slack.py +3028 -0
  242. package/gateway/platforms/sms.py +377 -0
  243. package/gateway/platforms/telegram.py +4836 -0
  244. package/gateway/platforms/telegram_network.py +249 -0
  245. package/gateway/platforms/webhook.py +806 -0
  246. package/gateway/platforms/wecom.py +1610 -0
  247. package/gateway/platforms/wecom_callback.py +403 -0
  248. package/gateway/platforms/wecom_crypto.py +142 -0
  249. package/gateway/platforms/weixin.py +2170 -0
  250. package/gateway/platforms/whatsapp.py +1283 -0
  251. package/gateway/platforms/yuanbao.py +4873 -0
  252. package/gateway/platforms/yuanbao_media.py +645 -0
  253. package/gateway/platforms/yuanbao_proto.py +1209 -0
  254. package/gateway/platforms/yuanbao_sticker.py +558 -0
  255. package/gateway/restart.py +20 -0
  256. package/gateway/run.py +17074 -0
  257. package/gateway/runtime_footer.py +150 -0
  258. package/gateway/session.py +1399 -0
  259. package/gateway/session_context.py +156 -0
  260. package/gateway/shutdown_forensics.py +462 -0
  261. package/gateway/slash_access.py +229 -0
  262. package/gateway/status.py +972 -0
  263. package/gateway/sticker_cache.py +111 -0
  264. package/gateway/stream_consumer.py +1286 -0
  265. package/gateway/whatsapp_identity.py +156 -0
  266. package/hermes_cli/__init__.py +47 -0
  267. package/hermes_cli/__pycache__/__init__.cpython-312.pyc +0 -0
  268. package/hermes_cli/__pycache__/_parser.cpython-312.pyc +0 -0
  269. package/hermes_cli/__pycache__/auth.cpython-312.pyc +0 -0
  270. package/hermes_cli/__pycache__/banner.cpython-312.pyc +0 -0
  271. package/hermes_cli/__pycache__/browser_connect.cpython-312.pyc +0 -0
  272. package/hermes_cli/__pycache__/callbacks.cpython-312.pyc +0 -0
  273. package/hermes_cli/__pycache__/checkpoints.cpython-312.pyc +0 -0
  274. package/hermes_cli/__pycache__/cli_output.cpython-312.pyc +0 -0
  275. package/hermes_cli/__pycache__/codex_models.cpython-312.pyc +0 -0
  276. package/hermes_cli/__pycache__/codex_runtime_switch.cpython-312.pyc +0 -0
  277. package/hermes_cli/__pycache__/colors.cpython-312.pyc +0 -0
  278. package/hermes_cli/__pycache__/commands.cpython-312.pyc +0 -0
  279. package/hermes_cli/__pycache__/config.cpython-312.pyc +0 -0
  280. package/hermes_cli/__pycache__/copilot_auth.cpython-312.pyc +0 -0
  281. package/hermes_cli/__pycache__/curator.cpython-312.pyc +0 -0
  282. package/hermes_cli/__pycache__/curses_ui.cpython-312.pyc +0 -0
  283. package/hermes_cli/__pycache__/debug.cpython-312.pyc +0 -0
  284. package/hermes_cli/__pycache__/default_soul.cpython-312.pyc +0 -0
  285. package/hermes_cli/__pycache__/env_loader.cpython-312.pyc +0 -0
  286. package/hermes_cli/__pycache__/fallback_cmd.cpython-312.pyc +0 -0
  287. package/hermes_cli/__pycache__/gateway.cpython-312.pyc +0 -0
  288. package/hermes_cli/__pycache__/gateway_windows.cpython-312.pyc +0 -0
  289. package/hermes_cli/__pycache__/goals.cpython-312.pyc +0 -0
  290. package/hermes_cli/__pycache__/inventory.cpython-312.pyc +0 -0
  291. package/hermes_cli/__pycache__/kanban.cpython-312.pyc +0 -0
  292. package/hermes_cli/__pycache__/kanban_db.cpython-312.pyc +0 -0
  293. package/hermes_cli/__pycache__/main.cpython-312.pyc +0 -0
  294. package/hermes_cli/__pycache__/model_catalog.cpython-312.pyc +0 -0
  295. package/hermes_cli/__pycache__/model_normalize.cpython-312.pyc +0 -0
  296. package/hermes_cli/__pycache__/model_switch.cpython-312.pyc +0 -0
  297. package/hermes_cli/__pycache__/models.cpython-312.pyc +0 -0
  298. package/hermes_cli/__pycache__/nous_subscription.cpython-312.pyc +0 -0
  299. package/hermes_cli/__pycache__/pairing.cpython-312.pyc +0 -0
  300. package/hermes_cli/__pycache__/platforms.cpython-312.pyc +0 -0
  301. package/hermes_cli/__pycache__/plugins.cpython-312.pyc +0 -0
  302. package/hermes_cli/__pycache__/profiles.cpython-312.pyc +0 -0
  303. package/hermes_cli/__pycache__/providers.cpython-312.pyc +0 -0
  304. package/hermes_cli/__pycache__/pt_input_extras.cpython-312.pyc +0 -0
  305. package/hermes_cli/__pycache__/runtime_provider.cpython-312.pyc +0 -0
  306. package/hermes_cli/__pycache__/security_advisories.cpython-312.pyc +0 -0
  307. package/hermes_cli/__pycache__/setup.cpython-312.pyc +0 -0
  308. package/hermes_cli/__pycache__/skills_hub.cpython-312.pyc +0 -0
  309. package/hermes_cli/__pycache__/skin_engine.cpython-312.pyc +0 -0
  310. package/hermes_cli/__pycache__/stdio.cpython-312.pyc +0 -0
  311. package/hermes_cli/__pycache__/timeouts.cpython-312.pyc +0 -0
  312. package/hermes_cli/__pycache__/tips.cpython-312.pyc +0 -0
  313. package/hermes_cli/__pycache__/tools_config.cpython-312.pyc +0 -0
  314. package/hermes_cli/__pycache__/voice.cpython-312.pyc +0 -0
  315. package/hermes_cli/_parser.py +365 -0
  316. package/hermes_cli/_subprocess_compat.py +175 -0
  317. package/hermes_cli/auth.py +6299 -0
  318. package/hermes_cli/auth_commands.py +749 -0
  319. package/hermes_cli/azure_detect.py +300 -0
  320. package/hermes_cli/backup.py +938 -0
  321. package/hermes_cli/banner.py +703 -0
  322. package/hermes_cli/browser_connect.py +139 -0
  323. package/hermes_cli/callbacks.py +243 -0
  324. package/hermes_cli/checkpoints.py +244 -0
  325. package/hermes_cli/claw.py +810 -0
  326. package/hermes_cli/cli_output.py +78 -0
  327. package/hermes_cli/clipboard.py +495 -0
  328. package/hermes_cli/codex_models.py +198 -0
  329. package/hermes_cli/codex_runtime_plugin_migration.py +757 -0
  330. package/hermes_cli/codex_runtime_switch.py +266 -0
  331. package/hermes_cli/colors.py +38 -0
  332. package/hermes_cli/commands.py +1728 -0
  333. package/hermes_cli/completion.py +315 -0
  334. package/hermes_cli/config.py +5382 -0
  335. package/hermes_cli/copilot_auth.py +392 -0
  336. package/hermes_cli/cron.py +313 -0
  337. package/hermes_cli/curator.py +598 -0
  338. package/hermes_cli/curses_ui.py +472 -0
  339. package/hermes_cli/debug.py +747 -0
  340. package/hermes_cli/default_soul.py +11 -0
  341. package/hermes_cli/dep_ensure.py +107 -0
  342. package/hermes_cli/dingtalk_auth.py +293 -0
  343. package/hermes_cli/doctor.py +1863 -0
  344. package/hermes_cli/dump.py +326 -0
  345. package/hermes_cli/env_loader.py +175 -0
  346. package/hermes_cli/fallback_cmd.py +361 -0
  347. package/hermes_cli/gateway.py +5422 -0
  348. package/hermes_cli/gateway_windows.py +692 -0
  349. package/hermes_cli/goals.py +757 -0
  350. package/hermes_cli/hooks.py +385 -0
  351. package/hermes_cli/inventory.py +240 -0
  352. package/hermes_cli/kanban.py +2252 -0
  353. package/hermes_cli/kanban_db.py +4840 -0
  354. package/hermes_cli/kanban_diagnostics.py +776 -0
  355. package/hermes_cli/kanban_specify.py +266 -0
  356. package/hermes_cli/logs.py +391 -0
  357. package/hermes_cli/main.py +12396 -0
  358. package/hermes_cli/mcp_config.py +781 -0
  359. package/hermes_cli/memory_setup.py +465 -0
  360. package/hermes_cli/model_catalog.py +330 -0
  361. package/hermes_cli/model_normalize.py +473 -0
  362. package/hermes_cli/model_switch.py +1777 -0
  363. package/hermes_cli/models.py +3789 -0
  364. package/hermes_cli/nous_subscription.py +799 -0
  365. package/hermes_cli/oneshot.py +351 -0
  366. package/hermes_cli/pairing.py +115 -0
  367. package/hermes_cli/platforms.py +83 -0
  368. package/hermes_cli/plugins.py +1562 -0
  369. package/hermes_cli/plugins_cmd.py +1587 -0
  370. package/hermes_cli/profile_distribution.py +703 -0
  371. package/hermes_cli/profiles.py +1319 -0
  372. package/hermes_cli/providers.py +720 -0
  373. package/hermes_cli/proxy/__init__.py +20 -0
  374. package/hermes_cli/proxy/adapters/__init__.py +35 -0
  375. package/hermes_cli/proxy/adapters/base.py +94 -0
  376. package/hermes_cli/proxy/adapters/nous_portal.py +137 -0
  377. package/hermes_cli/proxy/cli.py +141 -0
  378. package/hermes_cli/proxy/server.py +265 -0
  379. package/hermes_cli/pt_input_extras.py +83 -0
  380. package/hermes_cli/pty_bridge.py +237 -0
  381. package/hermes_cli/relaunch.py +205 -0
  382. package/hermes_cli/runtime_provider.py +1428 -0
  383. package/hermes_cli/security_advisories.py +452 -0
  384. package/hermes_cli/setup.py +3559 -0
  385. package/hermes_cli/skills_config.py +177 -0
  386. package/hermes_cli/skills_hub.py +1595 -0
  387. package/hermes_cli/skin_engine.py +929 -0
  388. package/hermes_cli/slack_cli.py +160 -0
  389. package/hermes_cli/status.py +550 -0
  390. package/hermes_cli/stdio.py +252 -0
  391. package/hermes_cli/timeouts.py +82 -0
  392. package/hermes_cli/tips.py +487 -0
  393. package/hermes_cli/tools_config.py +3151 -0
  394. package/hermes_cli/uninstall.py +681 -0
  395. package/hermes_cli/vercel_auth.py +70 -0
  396. package/hermes_cli/voice.py +846 -0
  397. package/hermes_cli/web_server.py +4438 -0
  398. package/hermes_cli/webhook.py +275 -0
  399. package/locales/af.yaml +350 -0
  400. package/locales/de.yaml +350 -0
  401. package/locales/en.yaml +365 -0
  402. package/locales/es.yaml +350 -0
  403. package/locales/fr.yaml +350 -0
  404. package/locales/ga.yaml +354 -0
  405. package/locales/hu.yaml +350 -0
  406. package/locales/it.yaml +350 -0
  407. package/locales/ja.yaml +350 -0
  408. package/locales/ko.yaml +350 -0
  409. package/locales/pt.yaml +350 -0
  410. package/locales/ru.yaml +350 -0
  411. package/locales/tr.yaml +350 -0
  412. package/locales/uk.yaml +350 -0
  413. package/locales/zh-hant.yaml +350 -0
  414. package/locales/zh.yaml +350 -0
  415. package/mcp_serve.py +898 -0
  416. package/model_tools.py +899 -0
  417. package/optional-skills/DESCRIPTION.md +24 -0
  418. package/optional-skills/autonomous-ai-agents/DESCRIPTION.md +2 -0
  419. package/optional-skills/autonomous-ai-agents/blackbox/SKILL.md +144 -0
  420. package/optional-skills/autonomous-ai-agents/honcho/SKILL.md +431 -0
  421. package/optional-skills/blockchain/evm/SKILL.md +211 -0
  422. package/optional-skills/blockchain/evm/scripts/evm_client.py +1508 -0
  423. package/optional-skills/blockchain/hyperliquid/SKILL.md +211 -0
  424. package/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py +1660 -0
  425. package/optional-skills/blockchain/solana/SKILL.md +208 -0
  426. package/optional-skills/blockchain/solana/scripts/solana_client.py +698 -0
  427. package/optional-skills/communication/DESCRIPTION.md +1 -0
  428. package/optional-skills/communication/one-three-one-rule/SKILL.md +104 -0
  429. package/optional-skills/creative/blender-mcp/SKILL.md +117 -0
  430. package/optional-skills/creative/concept-diagrams/SKILL.md +362 -0
  431. package/optional-skills/creative/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
  432. package/optional-skills/creative/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
  433. package/optional-skills/creative/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
  434. package/optional-skills/creative/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
  435. package/optional-skills/creative/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
  436. package/optional-skills/creative/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
  437. package/optional-skills/creative/concept-diagrams/examples/electricity-grid-flow.md +182 -0
  438. package/optional-skills/creative/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
  439. package/optional-skills/creative/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
  440. package/optional-skills/creative/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
  441. package/optional-skills/creative/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
  442. package/optional-skills/creative/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
  443. package/optional-skills/creative/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
  444. package/optional-skills/creative/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
  445. package/optional-skills/creative/concept-diagrams/examples/wind-turbine-structure.md +338 -0
  446. package/optional-skills/creative/concept-diagrams/references/dashboard-patterns.md +43 -0
  447. package/optional-skills/creative/concept-diagrams/references/infrastructure-patterns.md +144 -0
  448. package/optional-skills/creative/concept-diagrams/references/physical-shape-cookbook.md +42 -0
  449. package/optional-skills/creative/concept-diagrams/templates/template.html +174 -0
  450. package/optional-skills/creative/hyperframes/SKILL.md +191 -0
  451. package/optional-skills/creative/hyperframes/references/cli.md +185 -0
  452. package/optional-skills/creative/hyperframes/references/composition.md +129 -0
  453. package/optional-skills/creative/hyperframes/references/features.md +289 -0
  454. package/optional-skills/creative/hyperframes/references/gsap.md +136 -0
  455. package/optional-skills/creative/hyperframes/references/troubleshooting.md +137 -0
  456. package/optional-skills/creative/hyperframes/references/website-to-video.md +145 -0
  457. package/optional-skills/creative/hyperframes/scripts/setup.sh +135 -0
  458. package/optional-skills/creative/kanban-video-orchestrator/SKILL.md +207 -0
  459. package/optional-skills/creative/kanban-video-orchestrator/assets/brief.md.tmpl +79 -0
  460. package/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl +185 -0
  461. package/optional-skills/creative/kanban-video-orchestrator/assets/soul.md.tmpl +38 -0
  462. package/optional-skills/creative/kanban-video-orchestrator/references/examples.md +227 -0
  463. package/optional-skills/creative/kanban-video-orchestrator/references/intake.md +166 -0
  464. package/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md +276 -0
  465. package/optional-skills/creative/kanban-video-orchestrator/references/monitoring.md +180 -0
  466. package/optional-skills/creative/kanban-video-orchestrator/references/role-archetypes.md +298 -0
  467. package/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md +317 -0
  468. package/optional-skills/creative/kanban-video-orchestrator/scripts/bootstrap_pipeline.py +501 -0
  469. package/optional-skills/creative/kanban-video-orchestrator/scripts/monitor.py +195 -0
  470. package/optional-skills/creative/meme-generation/EXAMPLES.md +46 -0
  471. package/optional-skills/creative/meme-generation/SKILL.md +130 -0
  472. package/optional-skills/creative/meme-generation/scripts/generate_meme.py +471 -0
  473. package/optional-skills/creative/meme-generation/scripts/templates.json +97 -0
  474. package/optional-skills/devops/cli/SKILL.md +156 -0
  475. package/optional-skills/devops/cli/references/app-discovery.md +112 -0
  476. package/optional-skills/devops/cli/references/authentication.md +59 -0
  477. package/optional-skills/devops/cli/references/cli-reference.md +104 -0
  478. package/optional-skills/devops/cli/references/running-apps.md +171 -0
  479. package/optional-skills/devops/docker-management/SKILL.md +281 -0
  480. package/optional-skills/devops/pinggy-tunnel/SKILL.md +309 -0
  481. package/optional-skills/devops/watchers/SKILL.md +112 -0
  482. package/optional-skills/devops/watchers/scripts/_watermark.py +148 -0
  483. package/optional-skills/devops/watchers/scripts/watch_github.py +168 -0
  484. package/optional-skills/devops/watchers/scripts/watch_http_json.py +131 -0
  485. package/optional-skills/devops/watchers/scripts/watch_rss.py +121 -0
  486. package/optional-skills/dogfood/DESCRIPTION.md +3 -0
  487. package/optional-skills/dogfood/adversarial-ux-test/SKILL.md +191 -0
  488. package/optional-skills/email/agentmail/SKILL.md +126 -0
  489. package/optional-skills/finance/3-statement-model/SKILL.md +433 -0
  490. package/optional-skills/finance/3-statement-model/references/formatting.md +118 -0
  491. package/optional-skills/finance/3-statement-model/references/formulas.md +292 -0
  492. package/optional-skills/finance/3-statement-model/references/sec-filings.md +125 -0
  493. package/optional-skills/finance/comps-analysis/SKILL.md +662 -0
  494. package/optional-skills/finance/dcf-model/SKILL.md +1270 -0
  495. package/optional-skills/finance/dcf-model/TROUBLESHOOTING.md +40 -0
  496. package/optional-skills/finance/dcf-model/requirements.txt +7 -0
  497. package/optional-skills/finance/dcf-model/scripts/validate_dcf.py +292 -0
  498. package/optional-skills/finance/excel-author/SKILL.md +244 -0
  499. package/optional-skills/finance/excel-author/scripts/recalc.py +88 -0
  500. package/optional-skills/finance/lbo-model/SKILL.md +291 -0
  501. package/optional-skills/finance/merger-model/SKILL.md +144 -0
  502. package/optional-skills/finance/pptx-author/SKILL.md +173 -0
  503. package/optional-skills/finance/stocks/SKILL.md +95 -0
  504. package/optional-skills/finance/stocks/scripts/stocks_client.py +755 -0
  505. package/optional-skills/health/DESCRIPTION.md +1 -0
  506. package/optional-skills/health/fitness-nutrition/SKILL.md +256 -0
  507. package/optional-skills/health/fitness-nutrition/references/FORMULAS.md +100 -0
  508. package/optional-skills/health/fitness-nutrition/scripts/body_calc.py +210 -0
  509. package/optional-skills/health/fitness-nutrition/scripts/nutrition_search.py +86 -0
  510. package/optional-skills/health/neuroskill-bci/SKILL.md +459 -0
  511. package/optional-skills/health/neuroskill-bci/references/api.md +286 -0
  512. package/optional-skills/health/neuroskill-bci/references/metrics.md +220 -0
  513. package/optional-skills/health/neuroskill-bci/references/protocols.md +452 -0
  514. package/optional-skills/mcp/DESCRIPTION.md +3 -0
  515. package/optional-skills/mcp/fastmcp/SKILL.md +300 -0
  516. package/optional-skills/mcp/fastmcp/references/fastmcp-cli.md +110 -0
  517. package/optional-skills/mcp/fastmcp/scripts/scaffold_fastmcp.py +56 -0
  518. package/optional-skills/mcp/fastmcp/templates/api_wrapper.py +54 -0
  519. package/optional-skills/mcp/fastmcp/templates/database_server.py +77 -0
  520. package/optional-skills/mcp/fastmcp/templates/file_processor.py +55 -0
  521. package/optional-skills/mcp/mcporter/SKILL.md +123 -0
  522. package/optional-skills/migration/DESCRIPTION.md +2 -0
  523. package/optional-skills/migration/openclaw-migration/SKILL.md +298 -0
  524. package/optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py +3136 -0
  525. package/optional-skills/mlops/accelerate/SKILL.md +336 -0
  526. package/optional-skills/mlops/accelerate/references/custom-plugins.md +453 -0
  527. package/optional-skills/mlops/accelerate/references/megatron-integration.md +489 -0
  528. package/optional-skills/mlops/accelerate/references/performance.md +525 -0
  529. package/optional-skills/mlops/chroma/SKILL.md +410 -0
  530. package/optional-skills/mlops/chroma/references/integration.md +38 -0
  531. package/optional-skills/mlops/clip/SKILL.md +257 -0
  532. package/optional-skills/mlops/clip/references/applications.md +207 -0
  533. package/optional-skills/mlops/faiss/SKILL.md +225 -0
  534. package/optional-skills/mlops/faiss/references/index_types.md +280 -0
  535. package/optional-skills/mlops/flash-attention/SKILL.md +367 -0
  536. package/optional-skills/mlops/flash-attention/references/benchmarks.md +215 -0
  537. package/optional-skills/mlops/flash-attention/references/transformers-integration.md +293 -0
  538. package/optional-skills/mlops/guidance/SKILL.md +576 -0
  539. package/optional-skills/mlops/guidance/references/backends.md +554 -0
  540. package/optional-skills/mlops/guidance/references/constraints.md +674 -0
  541. package/optional-skills/mlops/guidance/references/examples.md +767 -0
  542. package/optional-skills/mlops/huggingface-tokenizers/SKILL.md +520 -0
  543. package/optional-skills/mlops/huggingface-tokenizers/references/algorithms.md +653 -0
  544. package/optional-skills/mlops/huggingface-tokenizers/references/integration.md +637 -0
  545. package/optional-skills/mlops/huggingface-tokenizers/references/pipeline.md +723 -0
  546. package/optional-skills/mlops/huggingface-tokenizers/references/training.md +565 -0
  547. package/optional-skills/mlops/inference/outlines/SKILL.md +656 -0
  548. package/optional-skills/mlops/inference/outlines/references/backends.md +615 -0
  549. package/optional-skills/mlops/inference/outlines/references/examples.md +773 -0
  550. package/optional-skills/mlops/inference/outlines/references/json_generation.md +652 -0
  551. package/optional-skills/mlops/instructor/SKILL.md +744 -0
  552. package/optional-skills/mlops/instructor/references/examples.md +107 -0
  553. package/optional-skills/mlops/instructor/references/providers.md +70 -0
  554. package/optional-skills/mlops/instructor/references/validation.md +606 -0
  555. package/optional-skills/mlops/lambda-labs/SKILL.md +549 -0
  556. package/optional-skills/mlops/lambda-labs/references/advanced-usage.md +611 -0
  557. package/optional-skills/mlops/lambda-labs/references/troubleshooting.md +530 -0
  558. package/optional-skills/mlops/llava/SKILL.md +308 -0
  559. package/optional-skills/mlops/llava/references/training.md +197 -0
  560. package/optional-skills/mlops/modal/SKILL.md +345 -0
  561. package/optional-skills/mlops/modal/references/advanced-usage.md +503 -0
  562. package/optional-skills/mlops/modal/references/troubleshooting.md +494 -0
  563. package/optional-skills/mlops/nemo-curator/SKILL.md +387 -0
  564. package/optional-skills/mlops/nemo-curator/references/deduplication.md +87 -0
  565. package/optional-skills/mlops/nemo-curator/references/filtering.md +102 -0
  566. package/optional-skills/mlops/peft/SKILL.md +435 -0
  567. package/optional-skills/mlops/peft/references/advanced-usage.md +514 -0
  568. package/optional-skills/mlops/peft/references/troubleshooting.md +480 -0
  569. package/optional-skills/mlops/pinecone/SKILL.md +362 -0
  570. package/optional-skills/mlops/pinecone/references/deployment.md +181 -0
  571. package/optional-skills/mlops/pytorch-fsdp/SKILL.md +130 -0
  572. package/optional-skills/mlops/pytorch-fsdp/references/index.md +7 -0
  573. package/optional-skills/mlops/pytorch-fsdp/references/other.md +4261 -0
  574. package/optional-skills/mlops/pytorch-lightning/SKILL.md +350 -0
  575. package/optional-skills/mlops/pytorch-lightning/references/callbacks.md +436 -0
  576. package/optional-skills/mlops/pytorch-lightning/references/distributed.md +490 -0
  577. package/optional-skills/mlops/pytorch-lightning/references/hyperparameter-tuning.md +556 -0
  578. package/optional-skills/mlops/qdrant/SKILL.md +497 -0
  579. package/optional-skills/mlops/qdrant/references/advanced-usage.md +648 -0
  580. package/optional-skills/mlops/qdrant/references/troubleshooting.md +631 -0
  581. package/optional-skills/mlops/saelens/SKILL.md +390 -0
  582. package/optional-skills/mlops/saelens/references/README.md +69 -0
  583. package/optional-skills/mlops/saelens/references/api.md +333 -0
  584. package/optional-skills/mlops/saelens/references/tutorials.md +318 -0
  585. package/optional-skills/mlops/simpo/SKILL.md +223 -0
  586. package/optional-skills/mlops/simpo/references/datasets.md +478 -0
  587. package/optional-skills/mlops/simpo/references/hyperparameters.md +452 -0
  588. package/optional-skills/mlops/simpo/references/loss-functions.md +350 -0
  589. package/optional-skills/mlops/slime/SKILL.md +468 -0
  590. package/optional-skills/mlops/slime/references/api-reference.md +392 -0
  591. package/optional-skills/mlops/slime/references/troubleshooting.md +386 -0
  592. package/optional-skills/mlops/stable-diffusion/SKILL.md +523 -0
  593. package/optional-skills/mlops/stable-diffusion/references/advanced-usage.md +716 -0
  594. package/optional-skills/mlops/stable-diffusion/references/troubleshooting.md +555 -0
  595. package/optional-skills/mlops/tensorrt-llm/SKILL.md +191 -0
  596. package/optional-skills/mlops/tensorrt-llm/references/multi-gpu.md +298 -0
  597. package/optional-skills/mlops/tensorrt-llm/references/optimization.md +242 -0
  598. package/optional-skills/mlops/tensorrt-llm/references/serving.md +470 -0
  599. package/optional-skills/mlops/torchtitan/SKILL.md +362 -0
  600. package/optional-skills/mlops/torchtitan/references/checkpoint.md +181 -0
  601. package/optional-skills/mlops/torchtitan/references/custom-models.md +258 -0
  602. package/optional-skills/mlops/torchtitan/references/float8.md +133 -0
  603. package/optional-skills/mlops/torchtitan/references/fsdp.md +126 -0
  604. package/optional-skills/mlops/training/axolotl/SKILL.md +166 -0
  605. package/optional-skills/mlops/training/axolotl/references/api.md +5548 -0
  606. package/optional-skills/mlops/training/axolotl/references/dataset-formats.md +1029 -0
  607. package/optional-skills/mlops/training/axolotl/references/index.md +15 -0
  608. package/optional-skills/mlops/training/axolotl/references/other.md +3563 -0
  609. package/optional-skills/mlops/training/trl-fine-tuning/SKILL.md +463 -0
  610. package/optional-skills/mlops/training/trl-fine-tuning/references/dpo-variants.md +227 -0
  611. package/optional-skills/mlops/training/trl-fine-tuning/references/grpo-training.md +504 -0
  612. package/optional-skills/mlops/training/trl-fine-tuning/references/online-rl.md +82 -0
  613. package/optional-skills/mlops/training/trl-fine-tuning/references/reward-modeling.md +122 -0
  614. package/optional-skills/mlops/training/trl-fine-tuning/references/sft-training.md +168 -0
  615. package/optional-skills/mlops/training/trl-fine-tuning/templates/basic_grpo_training.py +228 -0
  616. package/optional-skills/mlops/training/unsloth/SKILL.md +84 -0
  617. package/optional-skills/mlops/training/unsloth/references/index.md +7 -0
  618. package/optional-skills/mlops/training/unsloth/references/llms-full.md +16799 -0
  619. package/optional-skills/mlops/training/unsloth/references/llms-txt.md +12044 -0
  620. package/optional-skills/mlops/training/unsloth/references/llms.md +82 -0
  621. package/optional-skills/mlops/whisper/SKILL.md +321 -0
  622. package/optional-skills/mlops/whisper/references/languages.md +189 -0
  623. package/optional-skills/productivity/canvas/SKILL.md +98 -0
  624. package/optional-skills/productivity/canvas/scripts/canvas_api.py +157 -0
  625. package/optional-skills/productivity/here-now/SKILL.md +217 -0
  626. package/optional-skills/productivity/here-now/scripts/drive.sh +406 -0
  627. package/optional-skills/productivity/here-now/scripts/publish.sh +445 -0
  628. package/optional-skills/productivity/memento-flashcards/SKILL.md +324 -0
  629. package/optional-skills/productivity/memento-flashcards/scripts/memento_cards.py +353 -0
  630. package/optional-skills/productivity/memento-flashcards/scripts/youtube_quiz.py +88 -0
  631. package/optional-skills/productivity/shop-app/SKILL.md +340 -0
  632. package/optional-skills/productivity/shopify/SKILL.md +373 -0
  633. package/optional-skills/productivity/siyuan/SKILL.md +298 -0
  634. package/optional-skills/productivity/telephony/SKILL.md +418 -0
  635. package/optional-skills/productivity/telephony/scripts/telephony.py +1343 -0
  636. package/optional-skills/research/bioinformatics/SKILL.md +235 -0
  637. package/optional-skills/research/darwinian-evolver/SKILL.md +199 -0
  638. package/optional-skills/research/darwinian-evolver/scripts/parrot_openrouter.py +218 -0
  639. package/optional-skills/research/darwinian-evolver/scripts/show_snapshot.py +69 -0
  640. package/optional-skills/research/darwinian-evolver/templates/custom_problem_template.py +240 -0
  641. package/optional-skills/research/domain-intel/SKILL.md +97 -0
  642. package/optional-skills/research/domain-intel/scripts/domain_intel.py +397 -0
  643. package/optional-skills/research/drug-discovery/SKILL.md +227 -0
  644. package/optional-skills/research/drug-discovery/references/ADMET_REFERENCE.md +66 -0
  645. package/optional-skills/research/drug-discovery/scripts/chembl_target.py +53 -0
  646. package/optional-skills/research/drug-discovery/scripts/ro5_screen.py +44 -0
  647. package/optional-skills/research/duckduckgo-search/SKILL.md +238 -0
  648. package/optional-skills/research/duckduckgo-search/scripts/duckduckgo.sh +28 -0
  649. package/optional-skills/research/gitnexus-explorer/SKILL.md +214 -0
  650. package/optional-skills/research/gitnexus-explorer/scripts/proxy.mjs +92 -0
  651. package/optional-skills/research/osint-investigation/SKILL.md +277 -0
  652. package/optional-skills/research/osint-investigation/references/sources/courtlistener.md +98 -0
  653. package/optional-skills/research/osint-investigation/references/sources/gdelt.md +104 -0
  654. package/optional-skills/research/osint-investigation/references/sources/icij-offshore.md +104 -0
  655. package/optional-skills/research/osint-investigation/references/sources/nyc-acris.md +90 -0
  656. package/optional-skills/research/osint-investigation/references/sources/ofac-sdn.md +92 -0
  657. package/optional-skills/research/osint-investigation/references/sources/opencorporates.md +103 -0
  658. package/optional-skills/research/osint-investigation/references/sources/sec-edgar.md +83 -0
  659. package/optional-skills/research/osint-investigation/references/sources/senate-ld.md +89 -0
  660. package/optional-skills/research/osint-investigation/references/sources/usaspending.md +97 -0
  661. package/optional-skills/research/osint-investigation/references/sources/wayback.md +93 -0
  662. package/optional-skills/research/osint-investigation/references/sources/wikipedia.md +107 -0
  663. package/optional-skills/research/osint-investigation/scripts/_http.py +82 -0
  664. package/optional-skills/research/osint-investigation/scripts/_normalize.py +67 -0
  665. package/optional-skills/research/osint-investigation/scripts/build_findings.py +221 -0
  666. package/optional-skills/research/osint-investigation/scripts/entity_resolution.py +228 -0
  667. package/optional-skills/research/osint-investigation/scripts/fetch_courtlistener.py +149 -0
  668. package/optional-skills/research/osint-investigation/scripts/fetch_gdelt.py +162 -0
  669. package/optional-skills/research/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
  670. package/optional-skills/research/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
  671. package/optional-skills/research/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
  672. package/optional-skills/research/osint-investigation/scripts/fetch_opencorporates.py +192 -0
  673. package/optional-skills/research/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
  674. package/optional-skills/research/osint-investigation/scripts/fetch_senate_ld.py +146 -0
  675. package/optional-skills/research/osint-investigation/scripts/fetch_usaspending.py +170 -0
  676. package/optional-skills/research/osint-investigation/scripts/fetch_wayback.py +142 -0
  677. package/optional-skills/research/osint-investigation/scripts/fetch_wikipedia.py +267 -0
  678. package/optional-skills/research/osint-investigation/scripts/timing_analysis.py +253 -0
  679. package/optional-skills/research/osint-investigation/templates/source-template.md +59 -0
  680. package/optional-skills/research/parallel-cli/SKILL.md +391 -0
  681. package/optional-skills/research/qmd/SKILL.md +441 -0
  682. package/optional-skills/research/scrapling/SKILL.md +336 -0
  683. package/optional-skills/research/searxng-search/SKILL.md +212 -0
  684. package/optional-skills/research/searxng-search/scripts/searxng.sh +22 -0
  685. package/optional-skills/security/1password/SKILL.md +163 -0
  686. package/optional-skills/security/1password/references/cli-examples.md +31 -0
  687. package/optional-skills/security/1password/references/get-started.md +21 -0
  688. package/optional-skills/security/DESCRIPTION.md +3 -0
  689. package/optional-skills/security/oss-forensics/SKILL.md +423 -0
  690. package/optional-skills/security/oss-forensics/references/evidence-types.md +89 -0
  691. package/optional-skills/security/oss-forensics/references/github-archive-guide.md +184 -0
  692. package/optional-skills/security/oss-forensics/references/investigation-templates.md +131 -0
  693. package/optional-skills/security/oss-forensics/references/recovery-techniques.md +164 -0
  694. package/optional-skills/security/oss-forensics/scripts/evidence-store.py +313 -0
  695. package/optional-skills/security/oss-forensics/templates/forensic-report.md +151 -0
  696. package/optional-skills/security/oss-forensics/templates/malicious-package-report.md +43 -0
  697. package/optional-skills/security/sherlock/SKILL.md +193 -0
  698. package/optional-skills/software-development/rest-graphql-debug/SKILL.md +514 -0
  699. package/optional-skills/web-development/DESCRIPTION.md +5 -0
  700. package/optional-skills/web-development/page-agent/SKILL.md +190 -0
  701. package/package.json +78 -0
  702. package/plugins/__init__.py +1 -0
  703. package/plugins/__pycache__/__init__.cpython-312.pyc +0 -0
  704. package/plugins/context_engine/__init__.py +219 -0
  705. package/plugins/disk-cleanup/README.md +51 -0
  706. package/plugins/disk-cleanup/__init__.py +316 -0
  707. package/plugins/disk-cleanup/disk_cleanup.py +497 -0
  708. package/plugins/disk-cleanup/plugin.yaml +7 -0
  709. package/plugins/example-dashboard/dashboard/manifest.json +14 -0
  710. package/plugins/example-dashboard/dashboard/plugin_api.py +17 -0
  711. package/plugins/google_meet/README.md +131 -0
  712. package/plugins/google_meet/SKILL.md +148 -0
  713. package/plugins/google_meet/__init__.py +103 -0
  714. package/plugins/google_meet/audio_bridge.py +244 -0
  715. package/plugins/google_meet/cli.py +479 -0
  716. package/plugins/google_meet/meet_bot.py +852 -0
  717. package/plugins/google_meet/node/__init__.py +54 -0
  718. package/plugins/google_meet/node/cli.py +125 -0
  719. package/plugins/google_meet/node/client.py +107 -0
  720. package/plugins/google_meet/node/protocol.py +124 -0
  721. package/plugins/google_meet/node/registry.py +113 -0
  722. package/plugins/google_meet/node/server.py +201 -0
  723. package/plugins/google_meet/plugin.yaml +16 -0
  724. package/plugins/google_meet/process_manager.py +324 -0
  725. package/plugins/google_meet/realtime/__init__.py +10 -0
  726. package/plugins/google_meet/realtime/openai_client.py +332 -0
  727. package/plugins/google_meet/tools.py +348 -0
  728. package/plugins/hermes-achievements/LICENSE +21 -0
  729. package/plugins/hermes-achievements/README.md +150 -0
  730. package/plugins/hermes-achievements/dashboard/dist/index.js +732 -0
  731. package/plugins/hermes-achievements/dashboard/dist/style.css +146 -0
  732. package/plugins/hermes-achievements/dashboard/manifest.json +11 -0
  733. package/plugins/hermes-achievements/dashboard/plugin_api.py +1062 -0
  734. package/plugins/hermes-achievements/docs/achievements-performance-implementation-plan.md +157 -0
  735. package/plugins/hermes-achievements/docs/achievements-performance-implementation-spec.md +219 -0
  736. package/plugins/hermes-achievements/docs/achievements-performance-spec.md +174 -0
  737. package/plugins/hermes-achievements/docs/assets/achievements-dashboard-hd.png +0 -0
  738. package/plugins/hermes-achievements/docs/assets/achievements-tier-showcase-hd.png +0 -0
  739. package/plugins/hermes-achievements/tests/test_achievement_engine.py +156 -0
  740. package/plugins/image_gen/openai/__init__.py +303 -0
  741. package/plugins/image_gen/openai/__pycache__/__init__.cpython-312.pyc +0 -0
  742. package/plugins/image_gen/openai/plugin.yaml +7 -0
  743. package/plugins/image_gen/openai-codex/__init__.py +378 -0
  744. package/plugins/image_gen/openai-codex/__pycache__/__init__.cpython-312.pyc +0 -0
  745. package/plugins/image_gen/openai-codex/plugin.yaml +5 -0
  746. package/plugins/image_gen/xai/__init__.py +316 -0
  747. package/plugins/image_gen/xai/__pycache__/__init__.cpython-312.pyc +0 -0
  748. package/plugins/image_gen/xai/plugin.yaml +7 -0
  749. package/plugins/kanban/dashboard/dist/index.js +3143 -0
  750. package/plugins/kanban/dashboard/dist/style.css +1500 -0
  751. package/plugins/kanban/dashboard/manifest.json +14 -0
  752. package/plugins/kanban/dashboard/plugin_api.py +1612 -0
  753. package/plugins/kanban/systemd/hermes-kanban-dispatcher.service +32 -0
  754. package/plugins/memory/__init__.py +408 -0
  755. package/plugins/memory/byterover/README.md +41 -0
  756. package/plugins/memory/byterover/__init__.py +384 -0
  757. package/plugins/memory/byterover/plugin.yaml +9 -0
  758. package/plugins/memory/hindsight/README.md +138 -0
  759. package/plugins/memory/hindsight/__init__.py +1758 -0
  760. package/plugins/memory/hindsight/plugin.yaml +8 -0
  761. package/plugins/memory/holographic/README.md +36 -0
  762. package/plugins/memory/holographic/__init__.py +409 -0
  763. package/plugins/memory/holographic/holographic.py +203 -0
  764. package/plugins/memory/holographic/plugin.yaml +5 -0
  765. package/plugins/memory/holographic/retrieval.py +593 -0
  766. package/plugins/memory/holographic/store.py +579 -0
  767. package/plugins/memory/honcho/README.md +328 -0
  768. package/plugins/memory/honcho/__init__.py +1329 -0
  769. package/plugins/memory/honcho/cli.py +1452 -0
  770. package/plugins/memory/honcho/client.py +784 -0
  771. package/plugins/memory/honcho/plugin.yaml +7 -0
  772. package/plugins/memory/honcho/session.py +1255 -0
  773. package/plugins/memory/mem0/README.md +38 -0
  774. package/plugins/memory/mem0/__init__.py +374 -0
  775. package/plugins/memory/mem0/plugin.yaml +5 -0
  776. package/plugins/memory/openviking/README.md +40 -0
  777. package/plugins/memory/openviking/__init__.py +945 -0
  778. package/plugins/memory/openviking/plugin.yaml +9 -0
  779. package/plugins/memory/retaindb/README.md +40 -0
  780. package/plugins/memory/retaindb/__init__.py +767 -0
  781. package/plugins/memory/retaindb/plugin.yaml +7 -0
  782. package/plugins/memory/supermemory/README.md +99 -0
  783. package/plugins/memory/supermemory/__init__.py +792 -0
  784. package/plugins/memory/supermemory/plugin.yaml +5 -0
  785. package/plugins/model-providers/README.md +70 -0
  786. package/plugins/model-providers/ai-gateway/__init__.py +43 -0
  787. package/plugins/model-providers/ai-gateway/__pycache__/__init__.cpython-312.pyc +0 -0
  788. package/plugins/model-providers/ai-gateway/plugin.yaml +5 -0
  789. package/plugins/model-providers/alibaba/__init__.py +13 -0
  790. package/plugins/model-providers/alibaba/__pycache__/__init__.cpython-312.pyc +0 -0
  791. package/plugins/model-providers/alibaba/plugin.yaml +5 -0
  792. package/plugins/model-providers/alibaba-coding-plan/__init__.py +21 -0
  793. package/plugins/model-providers/alibaba-coding-plan/__pycache__/__init__.cpython-312.pyc +0 -0
  794. package/plugins/model-providers/alibaba-coding-plan/plugin.yaml +5 -0
  795. package/plugins/model-providers/anthropic/__init__.py +52 -0
  796. package/plugins/model-providers/anthropic/__pycache__/__init__.cpython-312.pyc +0 -0
  797. package/plugins/model-providers/anthropic/plugin.yaml +5 -0
  798. package/plugins/model-providers/arcee/__init__.py +13 -0
  799. package/plugins/model-providers/arcee/__pycache__/__init__.cpython-312.pyc +0 -0
  800. package/plugins/model-providers/arcee/plugin.yaml +5 -0
  801. package/plugins/model-providers/azure-foundry/__init__.py +21 -0
  802. package/plugins/model-providers/azure-foundry/__pycache__/__init__.cpython-312.pyc +0 -0
  803. package/plugins/model-providers/azure-foundry/plugin.yaml +5 -0
  804. package/plugins/model-providers/bedrock/__init__.py +29 -0
  805. package/plugins/model-providers/bedrock/__pycache__/__init__.cpython-312.pyc +0 -0
  806. package/plugins/model-providers/bedrock/plugin.yaml +5 -0
  807. package/plugins/model-providers/copilot/__init__.py +58 -0
  808. package/plugins/model-providers/copilot/__pycache__/__init__.cpython-312.pyc +0 -0
  809. package/plugins/model-providers/copilot/plugin.yaml +5 -0
  810. package/plugins/model-providers/copilot-acp/__init__.py +34 -0
  811. package/plugins/model-providers/copilot-acp/__pycache__/__init__.cpython-312.pyc +0 -0
  812. package/plugins/model-providers/copilot-acp/plugin.yaml +5 -0
  813. package/plugins/model-providers/custom/__init__.py +68 -0
  814. package/plugins/model-providers/custom/__pycache__/__init__.cpython-312.pyc +0 -0
  815. package/plugins/model-providers/custom/plugin.yaml +5 -0
  816. package/plugins/model-providers/deepseek/__init__.py +99 -0
  817. package/plugins/model-providers/deepseek/__pycache__/__init__.cpython-312.pyc +0 -0
  818. package/plugins/model-providers/deepseek/plugin.yaml +5 -0
  819. package/plugins/model-providers/gemini/__init__.py +72 -0
  820. package/plugins/model-providers/gemini/__pycache__/__init__.cpython-312.pyc +0 -0
  821. package/plugins/model-providers/gemini/plugin.yaml +5 -0
  822. package/plugins/model-providers/gmi/__init__.py +31 -0
  823. package/plugins/model-providers/gmi/__pycache__/__init__.cpython-312.pyc +0 -0
  824. package/plugins/model-providers/gmi/plugin.yaml +5 -0
  825. package/plugins/model-providers/huggingface/__init__.py +20 -0
  826. package/plugins/model-providers/huggingface/__pycache__/__init__.cpython-312.pyc +0 -0
  827. package/plugins/model-providers/huggingface/plugin.yaml +5 -0
  828. package/plugins/model-providers/kilocode/__init__.py +14 -0
  829. package/plugins/model-providers/kilocode/__pycache__/__init__.cpython-312.pyc +0 -0
  830. package/plugins/model-providers/kilocode/plugin.yaml +5 -0
  831. package/plugins/model-providers/kimi-coding/__init__.py +71 -0
  832. package/plugins/model-providers/kimi-coding/__pycache__/__init__.cpython-312.pyc +0 -0
  833. package/plugins/model-providers/kimi-coding/plugin.yaml +5 -0
  834. package/plugins/model-providers/minimax/__init__.py +45 -0
  835. package/plugins/model-providers/minimax/__pycache__/__init__.cpython-312.pyc +0 -0
  836. package/plugins/model-providers/minimax/plugin.yaml +5 -0
  837. package/plugins/model-providers/nous/__init__.py +54 -0
  838. package/plugins/model-providers/nous/__pycache__/__init__.cpython-312.pyc +0 -0
  839. package/plugins/model-providers/nous/plugin.yaml +5 -0
  840. package/plugins/model-providers/novita/__init__.py +27 -0
  841. package/plugins/model-providers/novita/__pycache__/__init__.cpython-312.pyc +0 -0
  842. package/plugins/model-providers/novita/plugin.yaml +5 -0
  843. package/plugins/model-providers/nvidia/__init__.py +21 -0
  844. package/plugins/model-providers/nvidia/__pycache__/__init__.cpython-312.pyc +0 -0
  845. package/plugins/model-providers/nvidia/plugin.yaml +5 -0
  846. package/plugins/model-providers/ollama-cloud/__init__.py +14 -0
  847. package/plugins/model-providers/ollama-cloud/__pycache__/__init__.cpython-312.pyc +0 -0
  848. package/plugins/model-providers/ollama-cloud/plugin.yaml +5 -0
  849. package/plugins/model-providers/openai-codex/__init__.py +15 -0
  850. package/plugins/model-providers/openai-codex/__pycache__/__init__.cpython-312.pyc +0 -0
  851. package/plugins/model-providers/openai-codex/plugin.yaml +5 -0
  852. package/plugins/model-providers/opencode-zen/__init__.py +30 -0
  853. package/plugins/model-providers/opencode-zen/__pycache__/__init__.cpython-312.pyc +0 -0
  854. package/plugins/model-providers/opencode-zen/plugin.yaml +5 -0
  855. package/plugins/model-providers/openrouter/__init__.py +115 -0
  856. package/plugins/model-providers/openrouter/__pycache__/__init__.cpython-312.pyc +0 -0
  857. package/plugins/model-providers/openrouter/plugin.yaml +5 -0
  858. package/plugins/model-providers/qwen-oauth/__init__.py +82 -0
  859. package/plugins/model-providers/qwen-oauth/__pycache__/__init__.cpython-312.pyc +0 -0
  860. package/plugins/model-providers/qwen-oauth/plugin.yaml +5 -0
  861. package/plugins/model-providers/stepfun/__init__.py +14 -0
  862. package/plugins/model-providers/stepfun/__pycache__/__init__.cpython-312.pyc +0 -0
  863. package/plugins/model-providers/stepfun/plugin.yaml +5 -0
  864. package/plugins/model-providers/xai/__init__.py +15 -0
  865. package/plugins/model-providers/xai/__pycache__/__init__.cpython-312.pyc +0 -0
  866. package/plugins/model-providers/xai/plugin.yaml +5 -0
  867. package/plugins/model-providers/xiaomi/__init__.py +14 -0
  868. package/plugins/model-providers/xiaomi/__pycache__/__init__.cpython-312.pyc +0 -0
  869. package/plugins/model-providers/xiaomi/plugin.yaml +5 -0
  870. package/plugins/model-providers/zai/__init__.py +21 -0
  871. package/plugins/model-providers/zai/__pycache__/__init__.cpython-312.pyc +0 -0
  872. package/plugins/model-providers/zai/plugin.yaml +5 -0
  873. package/plugins/observability/langfuse/README.md +53 -0
  874. package/plugins/observability/langfuse/__init__.py +1004 -0
  875. package/plugins/observability/langfuse/plugin.yaml +14 -0
  876. package/plugins/platforms/google_chat/__init__.py +3 -0
  877. package/plugins/platforms/google_chat/__pycache__/__init__.cpython-312.pyc +0 -0
  878. package/plugins/platforms/google_chat/__pycache__/adapter.cpython-312.pyc +0 -0
  879. package/plugins/platforms/google_chat/adapter.py +3343 -0
  880. package/plugins/platforms/google_chat/oauth.py +639 -0
  881. package/plugins/platforms/google_chat/plugin.yaml +39 -0
  882. package/plugins/platforms/irc/__init__.py +3 -0
  883. package/plugins/platforms/irc/__pycache__/__init__.cpython-312.pyc +0 -0
  884. package/plugins/platforms/irc/__pycache__/adapter.cpython-312.pyc +0 -0
  885. package/plugins/platforms/irc/adapter.py +969 -0
  886. package/plugins/platforms/irc/plugin.yaml +54 -0
  887. package/plugins/platforms/line/__init__.py +3 -0
  888. package/plugins/platforms/line/__pycache__/__init__.cpython-312.pyc +0 -0
  889. package/plugins/platforms/line/__pycache__/adapter.cpython-312.pyc +0 -0
  890. package/plugins/platforms/line/adapter.py +1639 -0
  891. package/plugins/platforms/line/plugin.yaml +65 -0
  892. package/plugins/platforms/simplex/__init__.py +3 -0
  893. package/plugins/platforms/simplex/__pycache__/__init__.cpython-312.pyc +0 -0
  894. package/plugins/platforms/simplex/__pycache__/adapter.cpython-312.pyc +0 -0
  895. package/plugins/platforms/simplex/adapter.py +746 -0
  896. package/plugins/platforms/simplex/plugin.yaml +37 -0
  897. package/plugins/platforms/teams/__init__.py +3 -0
  898. package/plugins/platforms/teams/__pycache__/__init__.cpython-312.pyc +0 -0
  899. package/plugins/platforms/teams/__pycache__/adapter.cpython-312.pyc +0 -0
  900. package/plugins/platforms/teams/adapter.py +1188 -0
  901. package/plugins/platforms/teams/plugin.yaml +48 -0
  902. package/plugins/spotify/__init__.py +66 -0
  903. package/plugins/spotify/__pycache__/__init__.cpython-312.pyc +0 -0
  904. package/plugins/spotify/__pycache__/client.cpython-312.pyc +0 -0
  905. package/plugins/spotify/__pycache__/tools.cpython-312.pyc +0 -0
  906. package/plugins/spotify/client.py +435 -0
  907. package/plugins/spotify/plugin.yaml +13 -0
  908. package/plugins/spotify/tools.py +454 -0
  909. package/plugins/teams_pipeline/__init__.py +23 -0
  910. package/plugins/teams_pipeline/cli.py +463 -0
  911. package/plugins/teams_pipeline/meetings.py +333 -0
  912. package/plugins/teams_pipeline/models.py +350 -0
  913. package/plugins/teams_pipeline/pipeline.py +692 -0
  914. package/plugins/teams_pipeline/plugin.yaml +9 -0
  915. package/plugins/teams_pipeline/runtime.py +135 -0
  916. package/plugins/teams_pipeline/store.py +194 -0
  917. package/plugins/teams_pipeline/subscriptions.py +249 -0
  918. package/plugins/video_gen/fal/__init__.py +523 -0
  919. package/plugins/video_gen/fal/__pycache__/__init__.cpython-312.pyc +0 -0
  920. package/plugins/video_gen/fal/plugin.yaml +7 -0
  921. package/plugins/video_gen/xai/__init__.py +441 -0
  922. package/plugins/video_gen/xai/__pycache__/__init__.cpython-312.pyc +0 -0
  923. package/plugins/video_gen/xai/plugin.yaml +7 -0
  924. package/plugins/web/__init__.py +7 -0
  925. package/plugins/web/__pycache__/__init__.cpython-312.pyc +0 -0
  926. package/plugins/web/brave_free/__init__.py +14 -0
  927. package/plugins/web/brave_free/__pycache__/__init__.cpython-312.pyc +0 -0
  928. package/plugins/web/brave_free/__pycache__/provider.cpython-312.pyc +0 -0
  929. package/plugins/web/brave_free/plugin.yaml +7 -0
  930. package/plugins/web/brave_free/provider.py +137 -0
  931. package/plugins/web/ddgs/__init__.py +15 -0
  932. package/plugins/web/ddgs/__pycache__/__init__.cpython-312.pyc +0 -0
  933. package/plugins/web/ddgs/__pycache__/provider.cpython-312.pyc +0 -0
  934. package/plugins/web/ddgs/plugin.yaml +7 -0
  935. package/plugins/web/ddgs/provider.py +104 -0
  936. package/plugins/web/exa/__init__.py +15 -0
  937. package/plugins/web/exa/__pycache__/__init__.cpython-312.pyc +0 -0
  938. package/plugins/web/exa/__pycache__/provider.cpython-312.pyc +0 -0
  939. package/plugins/web/exa/plugin.yaml +7 -0
  940. package/plugins/web/exa/provider.py +212 -0
  941. package/plugins/web/firecrawl/__init__.py +28 -0
  942. package/plugins/web/firecrawl/__pycache__/__init__.cpython-312.pyc +0 -0
  943. package/plugins/web/firecrawl/__pycache__/provider.cpython-312.pyc +0 -0
  944. package/plugins/web/firecrawl/plugin.yaml +7 -0
  945. package/plugins/web/firecrawl/provider.py +773 -0
  946. package/plugins/web/parallel/__init__.py +16 -0
  947. package/plugins/web/parallel/__pycache__/__init__.cpython-312.pyc +0 -0
  948. package/plugins/web/parallel/__pycache__/provider.cpython-312.pyc +0 -0
  949. package/plugins/web/parallel/plugin.yaml +7 -0
  950. package/plugins/web/parallel/provider.py +291 -0
  951. package/plugins/web/searxng/__init__.py +15 -0
  952. package/plugins/web/searxng/__pycache__/__init__.cpython-312.pyc +0 -0
  953. package/plugins/web/searxng/__pycache__/provider.cpython-312.pyc +0 -0
  954. package/plugins/web/searxng/plugin.yaml +7 -0
  955. package/plugins/web/searxng/provider.py +140 -0
  956. package/plugins/web/tavily/__init__.py +15 -0
  957. package/plugins/web/tavily/__pycache__/__init__.cpython-312.pyc +0 -0
  958. package/plugins/web/tavily/__pycache__/provider.cpython-312.pyc +0 -0
  959. package/plugins/web/tavily/plugin.yaml +7 -0
  960. package/plugins/web/tavily/provider.py +285 -0
  961. package/providers/README.md +78 -0
  962. package/providers/__init__.py +192 -0
  963. package/providers/__pycache__/__init__.cpython-312.pyc +0 -0
  964. package/providers/__pycache__/base.cpython-312.pyc +0 -0
  965. package/providers/base.py +184 -0
  966. package/pyproject.toml +255 -0
  967. package/run_agent.py +16409 -0
  968. package/scripts/benchmark_browser_eval.py +138 -0
  969. package/scripts/build_model_catalog.py +95 -0
  970. package/scripts/build_skills_index.py +325 -0
  971. package/scripts/check-windows-footguns.py +624 -0
  972. package/scripts/contributor_audit.py +473 -0
  973. package/scripts/discord-voice-doctor.py +396 -0
  974. package/scripts/hermes-gateway +416 -0
  975. package/scripts/install.cmd +28 -0
  976. package/scripts/install.ps1 +1611 -0
  977. package/scripts/install.sh +2007 -0
  978. package/scripts/install_psutil_android.py +117 -0
  979. package/scripts/keystroke_diagnostic.py +81 -0
  980. package/scripts/kill_modal.sh +34 -0
  981. package/scripts/lib/node-bootstrap.sh +238 -0
  982. package/scripts/lint_diff.py +207 -0
  983. package/scripts/postinstall.js +150 -0
  984. package/scripts/profile-tui.py +626 -0
  985. package/scripts/release.py +1680 -0
  986. package/scripts/run_tests.sh +129 -0
  987. package/scripts/sample_and_compress.py +409 -0
  988. package/scripts/setup_open_webui.sh +349 -0
  989. package/scripts/whatsapp-bridge/allowlist.js +88 -0
  990. package/scripts/whatsapp-bridge/allowlist.test.mjs +80 -0
  991. package/scripts/whatsapp-bridge/bridge.js +729 -0
  992. package/scripts/whatsapp-bridge/package-lock.json +2141 -0
  993. package/scripts/whatsapp-bridge/package.json +19 -0
  994. package/skills/apple/DESCRIPTION.md +2 -0
  995. package/skills/apple/apple-notes/SKILL.md +90 -0
  996. package/skills/apple/apple-reminders/SKILL.md +98 -0
  997. package/skills/apple/findmy/SKILL.md +131 -0
  998. package/skills/apple/imessage/SKILL.md +102 -0
  999. package/skills/apple/macos-computer-use/SKILL.md +201 -0
  1000. package/skills/autonomous-ai-agents/DESCRIPTION.md +3 -0
  1001. package/skills/autonomous-ai-agents/claude-code/SKILL.md +745 -0
  1002. package/skills/autonomous-ai-agents/codex/SKILL.md +130 -0
  1003. package/skills/autonomous-ai-agents/hermes-agent/SKILL.md +1014 -0
  1004. package/skills/autonomous-ai-agents/opencode/SKILL.md +219 -0
  1005. package/skills/creative/DESCRIPTION.md +3 -0
  1006. package/skills/creative/architecture-diagram/SKILL.md +148 -0
  1007. package/skills/creative/architecture-diagram/templates/template.html +319 -0
  1008. package/skills/creative/ascii-art/SKILL.md +322 -0
  1009. package/skills/creative/ascii-video/README.md +290 -0
  1010. package/skills/creative/ascii-video/SKILL.md +241 -0
  1011. package/skills/creative/ascii-video/references/architecture.md +802 -0
  1012. package/skills/creative/ascii-video/references/composition.md +892 -0
  1013. package/skills/creative/ascii-video/references/effects.md +1865 -0
  1014. package/skills/creative/ascii-video/references/inputs.md +685 -0
  1015. package/skills/creative/ascii-video/references/optimization.md +688 -0
  1016. package/skills/creative/ascii-video/references/scenes.md +1011 -0
  1017. package/skills/creative/ascii-video/references/shaders.md +1385 -0
  1018. package/skills/creative/ascii-video/references/troubleshooting.md +367 -0
  1019. package/skills/creative/baoyu-comic/PORT_NOTES.md +77 -0
  1020. package/skills/creative/baoyu-comic/SKILL.md +247 -0
  1021. package/skills/creative/baoyu-comic/references/analysis-framework.md +176 -0
  1022. package/skills/creative/baoyu-comic/references/art-styles/chalk.md +101 -0
  1023. package/skills/creative/baoyu-comic/references/art-styles/ink-brush.md +97 -0
  1024. package/skills/creative/baoyu-comic/references/art-styles/ligne-claire.md +75 -0
  1025. package/skills/creative/baoyu-comic/references/art-styles/manga.md +93 -0
  1026. package/skills/creative/baoyu-comic/references/art-styles/minimalist.md +84 -0
  1027. package/skills/creative/baoyu-comic/references/art-styles/realistic.md +89 -0
  1028. package/skills/creative/baoyu-comic/references/auto-selection.md +71 -0
  1029. package/skills/creative/baoyu-comic/references/base-prompt.md +98 -0
  1030. package/skills/creative/baoyu-comic/references/character-template.md +180 -0
  1031. package/skills/creative/baoyu-comic/references/layouts/cinematic.md +23 -0
  1032. package/skills/creative/baoyu-comic/references/layouts/dense.md +23 -0
  1033. package/skills/creative/baoyu-comic/references/layouts/four-panel.md +40 -0
  1034. package/skills/creative/baoyu-comic/references/layouts/mixed.md +23 -0
  1035. package/skills/creative/baoyu-comic/references/layouts/splash.md +23 -0
  1036. package/skills/creative/baoyu-comic/references/layouts/standard.md +23 -0
  1037. package/skills/creative/baoyu-comic/references/layouts/webtoon.md +30 -0
  1038. package/skills/creative/baoyu-comic/references/ohmsha-guide.md +85 -0
  1039. package/skills/creative/baoyu-comic/references/partial-workflows.md +106 -0
  1040. package/skills/creative/baoyu-comic/references/presets/concept-story.md +121 -0
  1041. package/skills/creative/baoyu-comic/references/presets/four-panel.md +107 -0
  1042. package/skills/creative/baoyu-comic/references/presets/ohmsha.md +114 -0
  1043. package/skills/creative/baoyu-comic/references/presets/shoujo.md +116 -0
  1044. package/skills/creative/baoyu-comic/references/presets/wuxia.md +110 -0
  1045. package/skills/creative/baoyu-comic/references/storyboard-template.md +143 -0
  1046. package/skills/creative/baoyu-comic/references/tones/action.md +110 -0
  1047. package/skills/creative/baoyu-comic/references/tones/dramatic.md +95 -0
  1048. package/skills/creative/baoyu-comic/references/tones/energetic.md +105 -0
  1049. package/skills/creative/baoyu-comic/references/tones/neutral.md +63 -0
  1050. package/skills/creative/baoyu-comic/references/tones/romantic.md +100 -0
  1051. package/skills/creative/baoyu-comic/references/tones/vintage.md +104 -0
  1052. package/skills/creative/baoyu-comic/references/tones/warm.md +94 -0
  1053. package/skills/creative/baoyu-comic/references/workflow.md +401 -0
  1054. package/skills/creative/baoyu-infographic/PORT_NOTES.md +43 -0
  1055. package/skills/creative/baoyu-infographic/SKILL.md +237 -0
  1056. package/skills/creative/baoyu-infographic/references/analysis-framework.md +182 -0
  1057. package/skills/creative/baoyu-infographic/references/base-prompt.md +43 -0
  1058. package/skills/creative/baoyu-infographic/references/layouts/bento-grid.md +41 -0
  1059. package/skills/creative/baoyu-infographic/references/layouts/binary-comparison.md +48 -0
  1060. package/skills/creative/baoyu-infographic/references/layouts/bridge.md +41 -0
  1061. package/skills/creative/baoyu-infographic/references/layouts/circular-flow.md +41 -0
  1062. package/skills/creative/baoyu-infographic/references/layouts/comic-strip.md +41 -0
  1063. package/skills/creative/baoyu-infographic/references/layouts/comparison-matrix.md +41 -0
  1064. package/skills/creative/baoyu-infographic/references/layouts/dashboard.md +41 -0
  1065. package/skills/creative/baoyu-infographic/references/layouts/dense-modules.md +72 -0
  1066. package/skills/creative/baoyu-infographic/references/layouts/funnel.md +41 -0
  1067. package/skills/creative/baoyu-infographic/references/layouts/hierarchical-layers.md +48 -0
  1068. package/skills/creative/baoyu-infographic/references/layouts/hub-spoke.md +41 -0
  1069. package/skills/creative/baoyu-infographic/references/layouts/iceberg.md +41 -0
  1070. package/skills/creative/baoyu-infographic/references/layouts/isometric-map.md +41 -0
  1071. package/skills/creative/baoyu-infographic/references/layouts/jigsaw.md +41 -0
  1072. package/skills/creative/baoyu-infographic/references/layouts/linear-progression.md +48 -0
  1073. package/skills/creative/baoyu-infographic/references/layouts/periodic-table.md +41 -0
  1074. package/skills/creative/baoyu-infographic/references/layouts/story-mountain.md +41 -0
  1075. package/skills/creative/baoyu-infographic/references/layouts/structural-breakdown.md +48 -0
  1076. package/skills/creative/baoyu-infographic/references/layouts/tree-branching.md +41 -0
  1077. package/skills/creative/baoyu-infographic/references/layouts/venn-diagram.md +41 -0
  1078. package/skills/creative/baoyu-infographic/references/layouts/winding-roadmap.md +41 -0
  1079. package/skills/creative/baoyu-infographic/references/structured-content-template.md +244 -0
  1080. package/skills/creative/baoyu-infographic/references/styles/aged-academia.md +36 -0
  1081. package/skills/creative/baoyu-infographic/references/styles/bold-graphic.md +36 -0
  1082. package/skills/creative/baoyu-infographic/references/styles/chalkboard.md +61 -0
  1083. package/skills/creative/baoyu-infographic/references/styles/claymation.md +29 -0
  1084. package/skills/creative/baoyu-infographic/references/styles/corporate-memphis.md +29 -0
  1085. package/skills/creative/baoyu-infographic/references/styles/craft-handmade.md +44 -0
  1086. package/skills/creative/baoyu-infographic/references/styles/cyberpunk-neon.md +29 -0
  1087. package/skills/creative/baoyu-infographic/references/styles/hand-drawn-edu.md +63 -0
  1088. package/skills/creative/baoyu-infographic/references/styles/ikea-manual.md +29 -0
  1089. package/skills/creative/baoyu-infographic/references/styles/kawaii.md +29 -0
  1090. package/skills/creative/baoyu-infographic/references/styles/knolling.md +29 -0
  1091. package/skills/creative/baoyu-infographic/references/styles/lego-brick.md +29 -0
  1092. package/skills/creative/baoyu-infographic/references/styles/morandi-journal.md +60 -0
  1093. package/skills/creative/baoyu-infographic/references/styles/origami.md +29 -0
  1094. package/skills/creative/baoyu-infographic/references/styles/pixel-art.md +29 -0
  1095. package/skills/creative/baoyu-infographic/references/styles/pop-laboratory.md +48 -0
  1096. package/skills/creative/baoyu-infographic/references/styles/retro-pop-grid.md +47 -0
  1097. package/skills/creative/baoyu-infographic/references/styles/storybook-watercolor.md +29 -0
  1098. package/skills/creative/baoyu-infographic/references/styles/subway-map.md +29 -0
  1099. package/skills/creative/baoyu-infographic/references/styles/technical-schematic.md +36 -0
  1100. package/skills/creative/baoyu-infographic/references/styles/ui-wireframe.md +29 -0
  1101. package/skills/creative/claude-design/SKILL.md +591 -0
  1102. package/skills/creative/comfyui/SKILL.md +612 -0
  1103. package/skills/creative/comfyui/references/official-cli.md +255 -0
  1104. package/skills/creative/comfyui/references/rest-api.md +312 -0
  1105. package/skills/creative/comfyui/references/template-integrity.md +243 -0
  1106. package/skills/creative/comfyui/references/workflow-format.md +226 -0
  1107. package/skills/creative/comfyui/scripts/_common.py +835 -0
  1108. package/skills/creative/comfyui/scripts/auto_fix_deps.py +225 -0
  1109. package/skills/creative/comfyui/scripts/check_deps.py +437 -0
  1110. package/skills/creative/comfyui/scripts/comfyui_setup.sh +286 -0
  1111. package/skills/creative/comfyui/scripts/extract_schema.py +315 -0
  1112. package/skills/creative/comfyui/scripts/fetch_logs.py +158 -0
  1113. package/skills/creative/comfyui/scripts/hardware_check.py +497 -0
  1114. package/skills/creative/comfyui/scripts/health_check.py +223 -0
  1115. package/skills/creative/comfyui/scripts/run_batch.py +243 -0
  1116. package/skills/creative/comfyui/scripts/run_workflow.py +796 -0
  1117. package/skills/creative/comfyui/scripts/ws_monitor.py +267 -0
  1118. package/skills/creative/comfyui/tests/README.md +50 -0
  1119. package/skills/creative/comfyui/tests/conftest.py +64 -0
  1120. package/skills/creative/comfyui/tests/pytest.ini +5 -0
  1121. package/skills/creative/comfyui/tests/test_check_deps.py +68 -0
  1122. package/skills/creative/comfyui/tests/test_cloud_integration.py +95 -0
  1123. package/skills/creative/comfyui/tests/test_common.py +447 -0
  1124. package/skills/creative/comfyui/tests/test_extract_schema.py +185 -0
  1125. package/skills/creative/comfyui/tests/test_run_workflow.py +213 -0
  1126. package/skills/creative/comfyui/workflows/README.md +86 -0
  1127. package/skills/creative/comfyui/workflows/animatediff_video.json +64 -0
  1128. package/skills/creative/comfyui/workflows/flux_dev_txt2img.json +78 -0
  1129. package/skills/creative/comfyui/workflows/sd15_txt2img.json +49 -0
  1130. package/skills/creative/comfyui/workflows/sdxl_img2img.json +54 -0
  1131. package/skills/creative/comfyui/workflows/sdxl_inpaint.json +59 -0
  1132. package/skills/creative/comfyui/workflows/sdxl_txt2img.json +49 -0
  1133. package/skills/creative/comfyui/workflows/upscale_4x.json +27 -0
  1134. package/skills/creative/comfyui/workflows/wan_video_t2v.json +69 -0
  1135. package/skills/creative/creative-ideation/SKILL.md +152 -0
  1136. package/skills/creative/creative-ideation/references/full-prompt-library.md +110 -0
  1137. package/skills/creative/design-md/SKILL.md +199 -0
  1138. package/skills/creative/design-md/templates/starter.md +99 -0
  1139. package/skills/creative/excalidraw/SKILL.md +199 -0
  1140. package/skills/creative/excalidraw/references/colors.md +44 -0
  1141. package/skills/creative/excalidraw/references/dark-mode.md +68 -0
  1142. package/skills/creative/excalidraw/references/examples.md +141 -0
  1143. package/skills/creative/excalidraw/scripts/upload.py +133 -0
  1144. package/skills/creative/humanizer/LICENSE +21 -0
  1145. package/skills/creative/humanizer/SKILL.md +578 -0
  1146. package/skills/creative/manim-video/README.md +23 -0
  1147. package/skills/creative/manim-video/SKILL.md +269 -0
  1148. package/skills/creative/manim-video/references/animation-design-thinking.md +161 -0
  1149. package/skills/creative/manim-video/references/animations.md +282 -0
  1150. package/skills/creative/manim-video/references/camera-and-3d.md +135 -0
  1151. package/skills/creative/manim-video/references/decorations.md +202 -0
  1152. package/skills/creative/manim-video/references/equations.md +216 -0
  1153. package/skills/creative/manim-video/references/graphs-and-data.md +163 -0
  1154. package/skills/creative/manim-video/references/mobjects.md +333 -0
  1155. package/skills/creative/manim-video/references/paper-explainer.md +255 -0
  1156. package/skills/creative/manim-video/references/production-quality.md +190 -0
  1157. package/skills/creative/manim-video/references/rendering.md +185 -0
  1158. package/skills/creative/manim-video/references/scene-planning.md +118 -0
  1159. package/skills/creative/manim-video/references/troubleshooting.md +135 -0
  1160. package/skills/creative/manim-video/references/updaters-and-trackers.md +260 -0
  1161. package/skills/creative/manim-video/references/visual-design.md +124 -0
  1162. package/skills/creative/manim-video/scripts/setup.sh +14 -0
  1163. package/skills/creative/p5js/README.md +64 -0
  1164. package/skills/creative/p5js/SKILL.md +556 -0
  1165. package/skills/creative/p5js/references/animation.md +439 -0
  1166. package/skills/creative/p5js/references/color-systems.md +352 -0
  1167. package/skills/creative/p5js/references/core-api.md +410 -0
  1168. package/skills/creative/p5js/references/export-pipeline.md +566 -0
  1169. package/skills/creative/p5js/references/interaction.md +398 -0
  1170. package/skills/creative/p5js/references/shapes-and-geometry.md +300 -0
  1171. package/skills/creative/p5js/references/troubleshooting.md +532 -0
  1172. package/skills/creative/p5js/references/typography.md +302 -0
  1173. package/skills/creative/p5js/references/visual-effects.md +895 -0
  1174. package/skills/creative/p5js/references/webgl-and-3d.md +423 -0
  1175. package/skills/creative/p5js/scripts/export-frames.js +179 -0
  1176. package/skills/creative/p5js/scripts/render.sh +108 -0
  1177. package/skills/creative/p5js/scripts/serve.sh +28 -0
  1178. package/skills/creative/p5js/scripts/setup.sh +87 -0
  1179. package/skills/creative/p5js/templates/viewer.html +395 -0
  1180. package/skills/creative/pixel-art/ATTRIBUTION.md +54 -0
  1181. package/skills/creative/pixel-art/SKILL.md +218 -0
  1182. package/skills/creative/pixel-art/references/palettes.md +49 -0
  1183. package/skills/creative/pixel-art/scripts/__init__.py +0 -0
  1184. package/skills/creative/pixel-art/scripts/palettes.py +167 -0
  1185. package/skills/creative/pixel-art/scripts/pixel_art.py +162 -0
  1186. package/skills/creative/pixel-art/scripts/pixel_art_video.py +345 -0
  1187. package/skills/creative/popular-web-designs/SKILL.md +214 -0
  1188. package/skills/creative/popular-web-designs/templates/airbnb.md +259 -0
  1189. package/skills/creative/popular-web-designs/templates/airtable.md +102 -0
  1190. package/skills/creative/popular-web-designs/templates/apple.md +326 -0
  1191. package/skills/creative/popular-web-designs/templates/bmw.md +193 -0
  1192. package/skills/creative/popular-web-designs/templates/cal.md +272 -0
  1193. package/skills/creative/popular-web-designs/templates/claude.md +325 -0
  1194. package/skills/creative/popular-web-designs/templates/clay.md +317 -0
  1195. package/skills/creative/popular-web-designs/templates/clickhouse.md +294 -0
  1196. package/skills/creative/popular-web-designs/templates/cohere.md +279 -0
  1197. package/skills/creative/popular-web-designs/templates/coinbase.md +142 -0
  1198. package/skills/creative/popular-web-designs/templates/composio.md +320 -0
  1199. package/skills/creative/popular-web-designs/templates/cursor.md +322 -0
  1200. package/skills/creative/popular-web-designs/templates/elevenlabs.md +278 -0
  1201. package/skills/creative/popular-web-designs/templates/expo.md +294 -0
  1202. package/skills/creative/popular-web-designs/templates/figma.md +233 -0
  1203. package/skills/creative/popular-web-designs/templates/framer.md +259 -0
  1204. package/skills/creative/popular-web-designs/templates/hashicorp.md +291 -0
  1205. package/skills/creative/popular-web-designs/templates/ibm.md +345 -0
  1206. package/skills/creative/popular-web-designs/templates/intercom.md +159 -0
  1207. package/skills/creative/popular-web-designs/templates/kraken.md +138 -0
  1208. package/skills/creative/popular-web-designs/templates/linear.app.md +380 -0
  1209. package/skills/creative/popular-web-designs/templates/lovable.md +311 -0
  1210. package/skills/creative/popular-web-designs/templates/minimax.md +270 -0
  1211. package/skills/creative/popular-web-designs/templates/mintlify.md +339 -0
  1212. package/skills/creative/popular-web-designs/templates/miro.md +121 -0
  1213. package/skills/creative/popular-web-designs/templates/mistral.ai.md +274 -0
  1214. package/skills/creative/popular-web-designs/templates/mongodb.md +279 -0
  1215. package/skills/creative/popular-web-designs/templates/notion.md +322 -0
  1216. package/skills/creative/popular-web-designs/templates/nvidia.md +306 -0
  1217. package/skills/creative/popular-web-designs/templates/ollama.md +280 -0
  1218. package/skills/creative/popular-web-designs/templates/opencode.ai.md +294 -0
  1219. package/skills/creative/popular-web-designs/templates/pinterest.md +243 -0
  1220. package/skills/creative/popular-web-designs/templates/posthog.md +269 -0
  1221. package/skills/creative/popular-web-designs/templates/raycast.md +281 -0
  1222. package/skills/creative/popular-web-designs/templates/replicate.md +274 -0
  1223. package/skills/creative/popular-web-designs/templates/resend.md +316 -0
  1224. package/skills/creative/popular-web-designs/templates/revolut.md +198 -0
  1225. package/skills/creative/popular-web-designs/templates/runwayml.md +257 -0
  1226. package/skills/creative/popular-web-designs/templates/sanity.md +370 -0
  1227. package/skills/creative/popular-web-designs/templates/sentry.md +275 -0
  1228. package/skills/creative/popular-web-designs/templates/spacex.md +207 -0
  1229. package/skills/creative/popular-web-designs/templates/spotify.md +259 -0
  1230. package/skills/creative/popular-web-designs/templates/stripe.md +335 -0
  1231. package/skills/creative/popular-web-designs/templates/supabase.md +268 -0
  1232. package/skills/creative/popular-web-designs/templates/superhuman.md +265 -0
  1233. package/skills/creative/popular-web-designs/templates/together.ai.md +276 -0
  1234. package/skills/creative/popular-web-designs/templates/uber.md +308 -0
  1235. package/skills/creative/popular-web-designs/templates/vercel.md +323 -0
  1236. package/skills/creative/popular-web-designs/templates/voltagent.md +336 -0
  1237. package/skills/creative/popular-web-designs/templates/warp.md +266 -0
  1238. package/skills/creative/popular-web-designs/templates/webflow.md +105 -0
  1239. package/skills/creative/popular-web-designs/templates/wise.md +186 -0
  1240. package/skills/creative/popular-web-designs/templates/x.ai.md +270 -0
  1241. package/skills/creative/popular-web-designs/templates/zapier.md +341 -0
  1242. package/skills/creative/pretext/SKILL.md +220 -0
  1243. package/skills/creative/pretext/references/patterns.md +258 -0
  1244. package/skills/creative/pretext/templates/donut-orbit.html +1468 -0
  1245. package/skills/creative/pretext/templates/hello-orb-flow.html +95 -0
  1246. package/skills/creative/sketch/SKILL.md +218 -0
  1247. package/skills/creative/songwriting-and-ai-music/SKILL.md +287 -0
  1248. package/skills/creative/touchdesigner-mcp/SKILL.md +356 -0
  1249. package/skills/creative/touchdesigner-mcp/references/3d-scene.md +275 -0
  1250. package/skills/creative/touchdesigner-mcp/references/animation.md +221 -0
  1251. package/skills/creative/touchdesigner-mcp/references/audio-reactive.md +175 -0
  1252. package/skills/creative/touchdesigner-mcp/references/dat-scripting.md +352 -0
  1253. package/skills/creative/touchdesigner-mcp/references/external-data.md +322 -0
  1254. package/skills/creative/touchdesigner-mcp/references/geometry-comp.md +121 -0
  1255. package/skills/creative/touchdesigner-mcp/references/glsl.md +151 -0
  1256. package/skills/creative/touchdesigner-mcp/references/layout-compositor.md +131 -0
  1257. package/skills/creative/touchdesigner-mcp/references/mcp-tools.md +382 -0
  1258. package/skills/creative/touchdesigner-mcp/references/midi-osc.md +211 -0
  1259. package/skills/creative/touchdesigner-mcp/references/network-patterns.md +966 -0
  1260. package/skills/creative/touchdesigner-mcp/references/operator-tips.md +106 -0
  1261. package/skills/creative/touchdesigner-mcp/references/operators.md +239 -0
  1262. package/skills/creative/touchdesigner-mcp/references/panel-ui.md +281 -0
  1263. package/skills/creative/touchdesigner-mcp/references/particles.md +245 -0
  1264. package/skills/creative/touchdesigner-mcp/references/pitfalls.md +704 -0
  1265. package/skills/creative/touchdesigner-mcp/references/postfx.md +183 -0
  1266. package/skills/creative/touchdesigner-mcp/references/projection-mapping.md +211 -0
  1267. package/skills/creative/touchdesigner-mcp/references/python-api.md +463 -0
  1268. package/skills/creative/touchdesigner-mcp/references/replicator.md +198 -0
  1269. package/skills/creative/touchdesigner-mcp/references/troubleshooting.md +244 -0
  1270. package/skills/creative/touchdesigner-mcp/scripts/setup.sh +115 -0
  1271. package/skills/data-science/DESCRIPTION.md +3 -0
  1272. package/skills/data-science/jupyter-live-kernel/SKILL.md +167 -0
  1273. package/skills/devops/kanban-orchestrator/SKILL.md +189 -0
  1274. package/skills/devops/kanban-worker/SKILL.md +184 -0
  1275. package/skills/devops/webhook-subscriptions/SKILL.md +204 -0
  1276. package/skills/diagramming/DESCRIPTION.md +3 -0
  1277. package/skills/dogfood/SKILL.md +162 -0
  1278. package/skills/dogfood/references/issue-taxonomy.md +109 -0
  1279. package/skills/dogfood/templates/dogfood-report-template.md +86 -0
  1280. package/skills/domain/DESCRIPTION.md +24 -0
  1281. package/skills/email/DESCRIPTION.md +3 -0
  1282. package/skills/email/himalaya/SKILL.md +299 -0
  1283. package/skills/email/himalaya/references/configuration.md +227 -0
  1284. package/skills/email/himalaya/references/message-composition.md +199 -0
  1285. package/skills/gaming/DESCRIPTION.md +3 -0
  1286. package/skills/gaming/minecraft-modpack-server/SKILL.md +187 -0
  1287. package/skills/gaming/pokemon-player/SKILL.md +216 -0
  1288. package/skills/gifs/DESCRIPTION.md +3 -0
  1289. package/skills/github/DESCRIPTION.md +3 -0
  1290. package/skills/github/codebase-inspection/SKILL.md +116 -0
  1291. package/skills/github/github-auth/SKILL.md +247 -0
  1292. package/skills/github/github-auth/scripts/gh-env.sh +66 -0
  1293. package/skills/github/github-code-review/SKILL.md +481 -0
  1294. package/skills/github/github-code-review/references/review-output-template.md +74 -0
  1295. package/skills/github/github-issues/SKILL.md +370 -0
  1296. package/skills/github/github-issues/templates/bug-report.md +35 -0
  1297. package/skills/github/github-issues/templates/feature-request.md +31 -0
  1298. package/skills/github/github-pr-workflow/SKILL.md +367 -0
  1299. package/skills/github/github-pr-workflow/references/ci-troubleshooting.md +183 -0
  1300. package/skills/github/github-pr-workflow/references/conventional-commits.md +71 -0
  1301. package/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
  1302. package/skills/github/github-pr-workflow/templates/pr-body-feature.md +33 -0
  1303. package/skills/github/github-repo-management/SKILL.md +516 -0
  1304. package/skills/github/github-repo-management/references/github-api-cheatsheet.md +161 -0
  1305. package/skills/index-cache/anthropics_skills_skills_.json +1 -0
  1306. package/skills/index-cache/claude_marketplace_anthropics_skills.json +1 -0
  1307. package/skills/index-cache/lobehub_index.json +1 -0
  1308. package/skills/index-cache/openai_skills_skills_.json +1 -0
  1309. package/skills/inference-sh/DESCRIPTION.md +19 -0
  1310. package/skills/mcp/DESCRIPTION.md +3 -0
  1311. package/skills/mcp/native-mcp/SKILL.md +357 -0
  1312. package/skills/media/DESCRIPTION.md +3 -0
  1313. package/skills/media/gif-search/SKILL.md +91 -0
  1314. package/skills/media/heartmula/SKILL.md +171 -0
  1315. package/skills/media/songsee/SKILL.md +83 -0
  1316. package/skills/media/spotify/SKILL.md +135 -0
  1317. package/skills/media/youtube-content/SKILL.md +73 -0
  1318. package/skills/media/youtube-content/references/output-formats.md +56 -0
  1319. package/skills/media/youtube-content/scripts/fetch_transcript.py +124 -0
  1320. package/skills/mlops/DESCRIPTION.md +3 -0
  1321. package/skills/mlops/evaluation/DESCRIPTION.md +3 -0
  1322. package/skills/mlops/evaluation/lm-evaluation-harness/SKILL.md +498 -0
  1323. package/skills/mlops/evaluation/lm-evaluation-harness/references/api-evaluation.md +490 -0
  1324. package/skills/mlops/evaluation/lm-evaluation-harness/references/benchmark-guide.md +488 -0
  1325. package/skills/mlops/evaluation/lm-evaluation-harness/references/custom-tasks.md +602 -0
  1326. package/skills/mlops/evaluation/lm-evaluation-harness/references/distributed-eval.md +519 -0
  1327. package/skills/mlops/evaluation/weights-and-biases/SKILL.md +594 -0
  1328. package/skills/mlops/evaluation/weights-and-biases/references/artifacts.md +584 -0
  1329. package/skills/mlops/evaluation/weights-and-biases/references/integrations.md +700 -0
  1330. package/skills/mlops/evaluation/weights-and-biases/references/sweeps.md +847 -0
  1331. package/skills/mlops/huggingface-hub/SKILL.md +81 -0
  1332. package/skills/mlops/inference/DESCRIPTION.md +3 -0
  1333. package/skills/mlops/inference/llama-cpp/SKILL.md +249 -0
  1334. package/skills/mlops/inference/llama-cpp/references/advanced-usage.md +504 -0
  1335. package/skills/mlops/inference/llama-cpp/references/hub-discovery.md +168 -0
  1336. package/skills/mlops/inference/llama-cpp/references/optimization.md +89 -0
  1337. package/skills/mlops/inference/llama-cpp/references/quantization.md +243 -0
  1338. package/skills/mlops/inference/llama-cpp/references/server.md +150 -0
  1339. package/skills/mlops/inference/llama-cpp/references/troubleshooting.md +442 -0
  1340. package/skills/mlops/inference/obliteratus/SKILL.md +342 -0
  1341. package/skills/mlops/inference/obliteratus/references/analysis-modules.md +166 -0
  1342. package/skills/mlops/inference/obliteratus/references/methods-guide.md +141 -0
  1343. package/skills/mlops/inference/obliteratus/templates/abliteration-config.yaml +33 -0
  1344. package/skills/mlops/inference/obliteratus/templates/analysis-study.yaml +40 -0
  1345. package/skills/mlops/inference/obliteratus/templates/batch-abliteration.yaml +41 -0
  1346. package/skills/mlops/inference/vllm/SKILL.md +372 -0
  1347. package/skills/mlops/inference/vllm/references/optimization.md +226 -0
  1348. package/skills/mlops/inference/vllm/references/quantization.md +284 -0
  1349. package/skills/mlops/inference/vllm/references/server-deployment.md +255 -0
  1350. package/skills/mlops/inference/vllm/references/troubleshooting.md +447 -0
  1351. package/skills/mlops/models/DESCRIPTION.md +3 -0
  1352. package/skills/mlops/models/audiocraft/SKILL.md +568 -0
  1353. package/skills/mlops/models/audiocraft/references/advanced-usage.md +666 -0
  1354. package/skills/mlops/models/audiocraft/references/troubleshooting.md +504 -0
  1355. package/skills/mlops/models/segment-anything/SKILL.md +506 -0
  1356. package/skills/mlops/models/segment-anything/references/advanced-usage.md +589 -0
  1357. package/skills/mlops/models/segment-anything/references/troubleshooting.md +484 -0
  1358. package/skills/mlops/research/DESCRIPTION.md +3 -0
  1359. package/skills/mlops/research/dspy/SKILL.md +594 -0
  1360. package/skills/mlops/research/dspy/references/examples.md +663 -0
  1361. package/skills/mlops/research/dspy/references/modules.md +475 -0
  1362. package/skills/mlops/research/dspy/references/optimizers.md +566 -0
  1363. package/skills/mlops/training/DESCRIPTION.md +3 -0
  1364. package/skills/mlops/vector-databases/DESCRIPTION.md +3 -0
  1365. package/skills/note-taking/DESCRIPTION.md +3 -0
  1366. package/skills/note-taking/obsidian/SKILL.md +61 -0
  1367. package/skills/productivity/DESCRIPTION.md +3 -0
  1368. package/skills/productivity/airtable/SKILL.md +229 -0
  1369. package/skills/productivity/google-workspace/SKILL.md +335 -0
  1370. package/skills/productivity/google-workspace/references/gmail-search-syntax.md +63 -0
  1371. package/skills/productivity/google-workspace/scripts/_hermes_home.py +43 -0
  1372. package/skills/productivity/google-workspace/scripts/google_api.py +1221 -0
  1373. package/skills/productivity/google-workspace/scripts/gws_bridge.py +108 -0
  1374. package/skills/productivity/google-workspace/scripts/setup.py +454 -0
  1375. package/skills/productivity/linear/SKILL.md +380 -0
  1376. package/skills/productivity/linear/scripts/linear_api.py +445 -0
  1377. package/skills/productivity/maps/SKILL.md +195 -0
  1378. package/skills/productivity/maps/scripts/maps_client.py +1298 -0
  1379. package/skills/productivity/nano-pdf/SKILL.md +52 -0
  1380. package/skills/productivity/notion/SKILL.md +448 -0
  1381. package/skills/productivity/notion/references/block-types.md +112 -0
  1382. package/skills/productivity/ocr-and-documents/DESCRIPTION.md +3 -0
  1383. package/skills/productivity/ocr-and-documents/SKILL.md +172 -0
  1384. package/skills/productivity/ocr-and-documents/scripts/extract_marker.py +87 -0
  1385. package/skills/productivity/ocr-and-documents/scripts/extract_pymupdf.py +98 -0
  1386. package/skills/productivity/powerpoint/LICENSE.txt +30 -0
  1387. package/skills/productivity/powerpoint/SKILL.md +237 -0
  1388. package/skills/productivity/powerpoint/editing.md +205 -0
  1389. package/skills/productivity/powerpoint/pptxgenjs.md +420 -0
  1390. package/skills/productivity/powerpoint/scripts/__init__.py +0 -0
  1391. package/skills/productivity/powerpoint/scripts/add_slide.py +195 -0
  1392. package/skills/productivity/powerpoint/scripts/clean.py +286 -0
  1393. package/skills/productivity/powerpoint/scripts/office/helpers/__init__.py +0 -0
  1394. package/skills/productivity/powerpoint/scripts/office/helpers/merge_runs.py +199 -0
  1395. package/skills/productivity/powerpoint/scripts/office/helpers/simplify_redlines.py +197 -0
  1396. package/skills/productivity/powerpoint/scripts/office/pack.py +159 -0
  1397. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  1398. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  1399. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  1400. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  1401. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  1402. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  1403. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  1404. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  1405. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  1406. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  1407. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  1408. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  1409. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  1410. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  1411. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  1412. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  1413. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  1414. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  1415. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  1416. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  1417. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  1418. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  1419. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  1420. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  1421. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  1422. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  1423. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  1424. package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-contentTypes.xsd +42 -0
  1425. package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-coreProperties.xsd +50 -0
  1426. package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-digSig.xsd +49 -0
  1427. package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-relationships.xsd +33 -0
  1428. package/skills/productivity/powerpoint/scripts/office/schemas/mce/mc.xsd +75 -0
  1429. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  1430. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  1431. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  1432. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  1433. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  1434. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  1435. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  1436. package/skills/productivity/teams-meeting-pipeline/SKILL.md +116 -0
  1437. package/skills/red-teaming/godmode/SKILL.md +404 -0
  1438. package/skills/red-teaming/godmode/references/jailbreak-templates.md +128 -0
  1439. package/skills/red-teaming/godmode/references/refusal-detection.md +142 -0
  1440. package/skills/red-teaming/godmode/scripts/auto_jailbreak.py +769 -0
  1441. package/skills/red-teaming/godmode/scripts/godmode_race.py +530 -0
  1442. package/skills/red-teaming/godmode/scripts/load_godmode.py +45 -0
  1443. package/skills/red-teaming/godmode/scripts/parseltongue.py +550 -0
  1444. package/skills/red-teaming/godmode/templates/prefill-subtle.json +10 -0
  1445. package/skills/red-teaming/godmode/templates/prefill.json +18 -0
  1446. package/skills/research/DESCRIPTION.md +3 -0
  1447. package/skills/research/arxiv/SKILL.md +282 -0
  1448. package/skills/research/arxiv/scripts/search_arxiv.py +114 -0
  1449. package/skills/research/blogwatcher/SKILL.md +137 -0
  1450. package/skills/research/llm-wiki/SKILL.md +507 -0
  1451. package/skills/research/polymarket/SKILL.md +77 -0
  1452. package/skills/research/polymarket/references/api-endpoints.md +220 -0
  1453. package/skills/research/polymarket/scripts/polymarket.py +284 -0
  1454. package/skills/research/research-paper-writing/SKILL.md +2377 -0
  1455. package/skills/research/research-paper-writing/references/autoreason-methodology.md +394 -0
  1456. package/skills/research/research-paper-writing/references/checklists.md +434 -0
  1457. package/skills/research/research-paper-writing/references/citation-workflow.md +564 -0
  1458. package/skills/research/research-paper-writing/references/experiment-patterns.md +728 -0
  1459. package/skills/research/research-paper-writing/references/human-evaluation.md +476 -0
  1460. package/skills/research/research-paper-writing/references/paper-types.md +481 -0
  1461. package/skills/research/research-paper-writing/references/reviewer-guidelines.md +433 -0
  1462. package/skills/research/research-paper-writing/references/sources.md +191 -0
  1463. package/skills/research/research-paper-writing/references/writing-guide.md +474 -0
  1464. package/skills/research/research-paper-writing/templates/README.md +251 -0
  1465. package/skills/research/research-paper-writing/templates/aaai2026/README.md +534 -0
  1466. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +144 -0
  1467. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +952 -0
  1468. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.bib +111 -0
  1469. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.bst +1493 -0
  1470. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.sty +315 -0
  1471. package/skills/research/research-paper-writing/templates/acl/README.md +50 -0
  1472. package/skills/research/research-paper-writing/templates/acl/acl.sty +312 -0
  1473. package/skills/research/research-paper-writing/templates/acl/acl_latex.tex +377 -0
  1474. package/skills/research/research-paper-writing/templates/acl/acl_lualatex.tex +101 -0
  1475. package/skills/research/research-paper-writing/templates/acl/acl_natbib.bst +1940 -0
  1476. package/skills/research/research-paper-writing/templates/acl/anthology.bib.txt +26 -0
  1477. package/skills/research/research-paper-writing/templates/acl/custom.bib +70 -0
  1478. package/skills/research/research-paper-writing/templates/acl/formatting.md +326 -0
  1479. package/skills/research/research-paper-writing/templates/colm2025/README.md +3 -0
  1480. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.bib +11 -0
  1481. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.bst +1440 -0
  1482. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
  1483. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.sty +218 -0
  1484. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.tex +305 -0
  1485. package/skills/research/research-paper-writing/templates/colm2025/fancyhdr.sty +485 -0
  1486. package/skills/research/research-paper-writing/templates/colm2025/math_commands.tex +508 -0
  1487. package/skills/research/research-paper-writing/templates/colm2025/natbib.sty +1246 -0
  1488. package/skills/research/research-paper-writing/templates/iclr2026/fancyhdr.sty +485 -0
  1489. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.bib +24 -0
  1490. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.bst +1440 -0
  1491. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
  1492. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.sty +246 -0
  1493. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.tex +414 -0
  1494. package/skills/research/research-paper-writing/templates/iclr2026/math_commands.tex +508 -0
  1495. package/skills/research/research-paper-writing/templates/iclr2026/natbib.sty +1246 -0
  1496. package/skills/research/research-paper-writing/templates/icml2026/algorithm.sty +79 -0
  1497. package/skills/research/research-paper-writing/templates/icml2026/algorithmic.sty +201 -0
  1498. package/skills/research/research-paper-writing/templates/icml2026/example_paper.bib +75 -0
  1499. package/skills/research/research-paper-writing/templates/icml2026/example_paper.pdf +0 -0
  1500. package/skills/research/research-paper-writing/templates/icml2026/example_paper.tex +662 -0
  1501. package/skills/research/research-paper-writing/templates/icml2026/fancyhdr.sty +864 -0
  1502. package/skills/research/research-paper-writing/templates/icml2026/icml2026.bst +1443 -0
  1503. package/skills/research/research-paper-writing/templates/icml2026/icml2026.sty +767 -0
  1504. package/skills/research/research-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
  1505. package/skills/research/research-paper-writing/templates/neurips2025/Makefile +36 -0
  1506. package/skills/research/research-paper-writing/templates/neurips2025/extra_pkgs.tex +53 -0
  1507. package/skills/research/research-paper-writing/templates/neurips2025/main.tex +38 -0
  1508. package/skills/research/research-paper-writing/templates/neurips2025/neurips.sty +382 -0
  1509. package/skills/smart-home/DESCRIPTION.md +3 -0
  1510. package/skills/smart-home/openhue/SKILL.md +109 -0
  1511. package/skills/social-media/DESCRIPTION.md +3 -0
  1512. package/skills/social-media/xurl/SKILL.md +414 -0
  1513. package/skills/software-development/debugging-hermes-tui-commands/SKILL.md +152 -0
  1514. package/skills/software-development/hermes-agent-skill-authoring/SKILL.md +165 -0
  1515. package/skills/software-development/node-inspect-debugger/SKILL.md +319 -0
  1516. package/skills/software-development/plan/SKILL.md +58 -0
  1517. package/skills/software-development/python-debugpy/SKILL.md +375 -0
  1518. package/skills/software-development/requesting-code-review/SKILL.md +280 -0
  1519. package/skills/software-development/spike/SKILL.md +197 -0
  1520. package/skills/software-development/subagent-driven-development/SKILL.md +352 -0
  1521. package/skills/software-development/subagent-driven-development/references/context-budget-discipline.md +53 -0
  1522. package/skills/software-development/subagent-driven-development/references/gates-taxonomy.md +93 -0
  1523. package/skills/software-development/systematic-debugging/SKILL.md +367 -0
  1524. package/skills/software-development/test-driven-development/SKILL.md +343 -0
  1525. package/skills/software-development/writing-plans/SKILL.md +297 -0
  1526. package/skills/yuanbao/SKILL.md +108 -0
  1527. package/tools/__init__.py +25 -0
  1528. package/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  1529. package/tools/__pycache__/approval.cpython-312.pyc +0 -0
  1530. package/tools/__pycache__/binary_extensions.cpython-312.pyc +0 -0
  1531. package/tools/__pycache__/browser_camofox.cpython-312.pyc +0 -0
  1532. package/tools/__pycache__/browser_camofox_state.cpython-312.pyc +0 -0
  1533. package/tools/__pycache__/browser_cdp_tool.cpython-312.pyc +0 -0
  1534. package/tools/__pycache__/browser_dialog_tool.cpython-312.pyc +0 -0
  1535. package/tools/__pycache__/browser_supervisor.cpython-312.pyc +0 -0
  1536. package/tools/__pycache__/browser_tool.cpython-312.pyc +0 -0
  1537. package/tools/__pycache__/budget_config.cpython-312.pyc +0 -0
  1538. package/tools/__pycache__/checkpoint_manager.cpython-312.pyc +0 -0
  1539. package/tools/__pycache__/clarify_gateway.cpython-312.pyc +0 -0
  1540. package/tools/__pycache__/clarify_tool.cpython-312.pyc +0 -0
  1541. package/tools/__pycache__/code_execution_tool.cpython-312.pyc +0 -0
  1542. package/tools/__pycache__/computer_use_tool.cpython-312.pyc +0 -0
  1543. package/tools/__pycache__/cronjob_tools.cpython-312.pyc +0 -0
  1544. package/tools/__pycache__/debug_helpers.cpython-312.pyc +0 -0
  1545. package/tools/__pycache__/delegate_tool.cpython-312.pyc +0 -0
  1546. package/tools/__pycache__/discord_tool.cpython-312.pyc +0 -0
  1547. package/tools/__pycache__/feishu_doc_tool.cpython-312.pyc +0 -0
  1548. package/tools/__pycache__/feishu_drive_tool.cpython-312.pyc +0 -0
  1549. package/tools/__pycache__/file_operations.cpython-312.pyc +0 -0
  1550. package/tools/__pycache__/file_state.cpython-312.pyc +0 -0
  1551. package/tools/__pycache__/file_tools.cpython-312.pyc +0 -0
  1552. package/tools/__pycache__/homeassistant_tool.cpython-312.pyc +0 -0
  1553. package/tools/__pycache__/image_generation_tool.cpython-312.pyc +0 -0
  1554. package/tools/__pycache__/interrupt.cpython-312.pyc +0 -0
  1555. package/tools/__pycache__/kanban_tools.cpython-312.pyc +0 -0
  1556. package/tools/__pycache__/lazy_deps.cpython-312.pyc +0 -0
  1557. package/tools/__pycache__/managed_tool_gateway.cpython-312.pyc +0 -0
  1558. package/tools/__pycache__/mcp_tool.cpython-312.pyc +0 -0
  1559. package/tools/__pycache__/memory_tool.cpython-312.pyc +0 -0
  1560. package/tools/__pycache__/mixture_of_agents_tool.cpython-312.pyc +0 -0
  1561. package/tools/__pycache__/openrouter_client.cpython-312.pyc +0 -0
  1562. package/tools/__pycache__/process_registry.cpython-312.pyc +0 -0
  1563. package/tools/__pycache__/registry.cpython-312.pyc +0 -0
  1564. package/tools/__pycache__/schema_sanitizer.cpython-312.pyc +0 -0
  1565. package/tools/__pycache__/send_message_tool.cpython-312.pyc +0 -0
  1566. package/tools/__pycache__/session_search_tool.cpython-312.pyc +0 -0
  1567. package/tools/__pycache__/skill_manager_tool.cpython-312.pyc +0 -0
  1568. package/tools/__pycache__/skill_provenance.cpython-312.pyc +0 -0
  1569. package/tools/__pycache__/skill_usage.cpython-312.pyc +0 -0
  1570. package/tools/__pycache__/skills_guard.cpython-312.pyc +0 -0
  1571. package/tools/__pycache__/skills_sync.cpython-312.pyc +0 -0
  1572. package/tools/__pycache__/skills_tool.cpython-312.pyc +0 -0
  1573. package/tools/__pycache__/slash_confirm.cpython-312.pyc +0 -0
  1574. package/tools/__pycache__/terminal_tool.cpython-312.pyc +0 -0
  1575. package/tools/__pycache__/tirith_security.cpython-312.pyc +0 -0
  1576. package/tools/__pycache__/todo_tool.cpython-312.pyc +0 -0
  1577. package/tools/__pycache__/tool_backend_helpers.cpython-312.pyc +0 -0
  1578. package/tools/__pycache__/tool_result_storage.cpython-312.pyc +0 -0
  1579. package/tools/__pycache__/tts_tool.cpython-312.pyc +0 -0
  1580. package/tools/__pycache__/url_safety.cpython-312.pyc +0 -0
  1581. package/tools/__pycache__/video_generation_tool.cpython-312.pyc +0 -0
  1582. package/tools/__pycache__/vision_tools.cpython-312.pyc +0 -0
  1583. package/tools/__pycache__/voice_mode.cpython-312.pyc +0 -0
  1584. package/tools/__pycache__/web_tools.cpython-312.pyc +0 -0
  1585. package/tools/__pycache__/website_policy.cpython-312.pyc +0 -0
  1586. package/tools/__pycache__/x_search_tool.cpython-312.pyc +0 -0
  1587. package/tools/__pycache__/xai_http.cpython-312.pyc +0 -0
  1588. package/tools/__pycache__/yuanbao_tools.cpython-312.pyc +0 -0
  1589. package/tools/ansi_strip.py +44 -0
  1590. package/tools/approval.py +1392 -0
  1591. package/tools/binary_extensions.py +42 -0
  1592. package/tools/browser_camofox.py +700 -0
  1593. package/tools/browser_camofox_state.py +48 -0
  1594. package/tools/browser_cdp_tool.py +569 -0
  1595. package/tools/browser_dialog_tool.py +148 -0
  1596. package/tools/browser_providers/__init__.py +10 -0
  1597. package/tools/browser_providers/__pycache__/__init__.cpython-312.pyc +0 -0
  1598. package/tools/browser_providers/__pycache__/base.cpython-312.pyc +0 -0
  1599. package/tools/browser_providers/__pycache__/browser_use.cpython-312.pyc +0 -0
  1600. package/tools/browser_providers/__pycache__/browserbase.cpython-312.pyc +0 -0
  1601. package/tools/browser_providers/__pycache__/firecrawl.cpython-312.pyc +0 -0
  1602. package/tools/browser_providers/base.py +59 -0
  1603. package/tools/browser_providers/browser_use.py +225 -0
  1604. package/tools/browser_providers/browserbase.py +222 -0
  1605. package/tools/browser_providers/firecrawl.py +112 -0
  1606. package/tools/browser_supervisor.py +1457 -0
  1607. package/tools/browser_tool.py +3676 -0
  1608. package/tools/budget_config.py +51 -0
  1609. package/tools/checkpoint_manager.py +1639 -0
  1610. package/tools/clarify_gateway.py +278 -0
  1611. package/tools/clarify_tool.py +141 -0
  1612. package/tools/code_execution_tool.py +1782 -0
  1613. package/tools/computer_use/__init__.py +43 -0
  1614. package/tools/computer_use/__pycache__/__init__.cpython-312.pyc +0 -0
  1615. package/tools/computer_use/__pycache__/backend.cpython-312.pyc +0 -0
  1616. package/tools/computer_use/__pycache__/schema.cpython-312.pyc +0 -0
  1617. package/tools/computer_use/__pycache__/tool.cpython-312.pyc +0 -0
  1618. package/tools/computer_use/backend.py +150 -0
  1619. package/tools/computer_use/cua_backend.py +682 -0
  1620. package/tools/computer_use/schema.py +191 -0
  1621. package/tools/computer_use/tool.py +521 -0
  1622. package/tools/computer_use_tool.py +39 -0
  1623. package/tools/credential_files.py +437 -0
  1624. package/tools/cronjob_tools.py +719 -0
  1625. package/tools/debug_helpers.py +106 -0
  1626. package/tools/delegate_tool.py +2797 -0
  1627. package/tools/discord_tool.py +959 -0
  1628. package/tools/env_passthrough.py +145 -0
  1629. package/tools/environments/__init__.py +14 -0
  1630. package/tools/environments/__pycache__/__init__.cpython-312.pyc +0 -0
  1631. package/tools/environments/__pycache__/base.cpython-312.pyc +0 -0
  1632. package/tools/environments/__pycache__/docker.cpython-312.pyc +0 -0
  1633. package/tools/environments/__pycache__/file_sync.cpython-312.pyc +0 -0
  1634. package/tools/environments/__pycache__/local.cpython-312.pyc +0 -0
  1635. package/tools/environments/__pycache__/managed_modal.cpython-312.pyc +0 -0
  1636. package/tools/environments/__pycache__/modal.cpython-312.pyc +0 -0
  1637. package/tools/environments/__pycache__/modal_utils.cpython-312.pyc +0 -0
  1638. package/tools/environments/__pycache__/singularity.cpython-312.pyc +0 -0
  1639. package/tools/environments/__pycache__/ssh.cpython-312.pyc +0 -0
  1640. package/tools/environments/base.py +844 -0
  1641. package/tools/environments/daytona.py +270 -0
  1642. package/tools/environments/docker.py +656 -0
  1643. package/tools/environments/file_sync.py +400 -0
  1644. package/tools/environments/local.py +658 -0
  1645. package/tools/environments/managed_modal.py +282 -0
  1646. package/tools/environments/modal.py +479 -0
  1647. package/tools/environments/modal_utils.py +199 -0
  1648. package/tools/environments/singularity.py +263 -0
  1649. package/tools/environments/ssh.py +295 -0
  1650. package/tools/environments/vercel_sandbox.py +655 -0
  1651. package/tools/feishu_doc_tool.py +138 -0
  1652. package/tools/feishu_drive_tool.py +431 -0
  1653. package/tools/file_operations.py +1825 -0
  1654. package/tools/file_state.py +332 -0
  1655. package/tools/file_tools.py +1172 -0
  1656. package/tools/fuzzy_match.py +703 -0
  1657. package/tools/homeassistant_tool.py +513 -0
  1658. package/tools/image_generation_tool.py +1098 -0
  1659. package/tools/interrupt.py +98 -0
  1660. package/tools/kanban_tools.py +1139 -0
  1661. package/tools/lazy_deps.py +608 -0
  1662. package/tools/managed_tool_gateway.py +168 -0
  1663. package/tools/mcp_oauth.py +633 -0
  1664. package/tools/mcp_oauth_manager.py +607 -0
  1665. package/tools/mcp_tool.py +3483 -0
  1666. package/tools/memory_tool.py +584 -0
  1667. package/tools/microsoft_graph_auth.py +245 -0
  1668. package/tools/microsoft_graph_client.py +408 -0
  1669. package/tools/mixture_of_agents_tool.py +542 -0
  1670. package/tools/neutts_samples/jo.txt +1 -0
  1671. package/tools/neutts_samples/jo.wav +0 -0
  1672. package/tools/neutts_synth.py +104 -0
  1673. package/tools/openrouter_client.py +33 -0
  1674. package/tools/osv_check.py +155 -0
  1675. package/tools/patch_parser.py +592 -0
  1676. package/tools/path_security.py +43 -0
  1677. package/tools/process_registry.py +1534 -0
  1678. package/tools/registry.py +589 -0
  1679. package/tools/schema_sanitizer.py +370 -0
  1680. package/tools/send_message_tool.py +1900 -0
  1681. package/tools/session_search_tool.py +613 -0
  1682. package/tools/skill_manager_tool.py +932 -0
  1683. package/tools/skill_provenance.py +78 -0
  1684. package/tools/skill_usage.py +610 -0
  1685. package/tools/skills_guard.py +932 -0
  1686. package/tools/skills_hub.py +3263 -0
  1687. package/tools/skills_sync.py +432 -0
  1688. package/tools/skills_tool.py +1569 -0
  1689. package/tools/slash_confirm.py +167 -0
  1690. package/tools/terminal_tool.py +2376 -0
  1691. package/tools/tirith_security.py +775 -0
  1692. package/tools/todo_tool.py +277 -0
  1693. package/tools/tool_backend_helpers.py +144 -0
  1694. package/tools/tool_output_limits.py +92 -0
  1695. package/tools/tool_result_storage.py +232 -0
  1696. package/tools/transcription_tools.py +936 -0
  1697. package/tools/tts_tool.py +2285 -0
  1698. package/tools/url_safety.py +330 -0
  1699. package/tools/video_generation_tool.py +561 -0
  1700. package/tools/vision_tools.py +1422 -0
  1701. package/tools/voice_mode.py +1019 -0
  1702. package/tools/web_tools.py +1551 -0
  1703. package/tools/website_policy.py +283 -0
  1704. package/tools/x_search_tool.py +424 -0
  1705. package/tools/xai_http.py +83 -0
  1706. package/tools/yuanbao_tools.py +736 -0
  1707. package/toolset_distributions.py +364 -0
  1708. package/toolsets.py +866 -0
  1709. package/trajectory_compressor.py +1509 -0
  1710. package/tui_gateway/__init__.py +0 -0
  1711. package/tui_gateway/entry.py +251 -0
  1712. package/tui_gateway/event_publisher.py +126 -0
  1713. package/tui_gateway/render.py +49 -0
  1714. package/tui_gateway/server.py +6623 -0
  1715. package/tui_gateway/slash_worker.py +76 -0
  1716. package/tui_gateway/transport.py +219 -0
  1717. package/tui_gateway/ws.py +178 -0
  1718. package/utils.py +361 -0
@@ -0,0 +1,2797 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Delegate Tool -- Subagent Architecture
4
+
5
+ Spawns child AIAgent instances with isolated context, restricted toolsets,
6
+ and their own terminal sessions. Supports single-task and batch (parallel)
7
+ modes. The parent blocks until all children complete.
8
+
9
+ Each child gets:
10
+ - A fresh conversation (no parent history)
11
+ - Its own task_id (own terminal session, file ops cache)
12
+ - A restricted toolset (configurable, with blocked tools always stripped)
13
+ - A focused system prompt built from the delegated goal + context
14
+
15
+ The parent's context only sees the delegation call and the summary result,
16
+ never the child's intermediate tool calls or reasoning.
17
+ """
18
+
19
+ import enum
20
+ import json
21
+ import logging
22
+
23
+ logger = logging.getLogger(__name__)
24
+ import os
25
+ import threading
26
+ import time
27
+ from concurrent.futures import (
28
+ ThreadPoolExecutor,
29
+ TimeoutError as FuturesTimeoutError,
30
+ )
31
+ from typing import Any, Dict, List, Optional
32
+
33
+ from toolsets import TOOLSETS
34
+ from tools import file_state
35
+ from tools.terminal_tool import set_approval_callback as _set_subagent_approval_cb
36
+ from utils import base_url_hostname, is_truthy_value
37
+
38
+
39
+ # Tools that children must never have access to
40
+ DELEGATE_BLOCKED_TOOLS = frozenset(
41
+ [
42
+ "delegate_task", # no recursive delegation
43
+ "clarify", # no user interaction
44
+ "memory", # no writes to shared MEMORY.md
45
+ "send_message", # no cross-platform side effects
46
+ "execute_code", # children should reason step-by-step, not write scripts
47
+ ]
48
+ )
49
+
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Subagent approval callbacks
53
+ # ---------------------------------------------------------------------------
54
+ # Subagents run inside a ThreadPoolExecutor worker. The CLI's interactive
55
+ # approval callback is stored in tools/terminal_tool.py's threading.local(),
56
+ # so worker threads do NOT inherit it. Without a callback,
57
+ # prompt_dangerous_approval() falls back to input() from the worker thread,
58
+ # which deadlocks against the parent's prompt_toolkit TUI that owns stdin.
59
+ #
60
+ # Fix: install a non-interactive callback into every subagent worker thread
61
+ # via ThreadPoolExecutor(initializer=_set_subagent_approval_cb, initargs=(cb,)).
62
+ # The callback is chosen by the `delegation.subagent_auto_approve` config:
63
+ # false (default) → _subagent_auto_deny (safe; matches leaf tool blocklist)
64
+ # true → _subagent_auto_approve (opt-in YOLO for cron/batch)
65
+ # Both emit a logger.warning for audit; gateway sessions are unaffected
66
+ # because they resolve approvals via tools/approval.py's per-session queue,
67
+ # not through these TLS callbacks.
68
+ def _subagent_auto_deny(command: str, description: str, **kwargs) -> str:
69
+ """Auto-deny dangerous commands in subagent threads (safe default).
70
+
71
+ Returns 'deny' so the subagent sees a refusal it can recover from, and
72
+ never calls input() (which would deadlock the parent TUI).
73
+ """
74
+ logger.warning(
75
+ "Subagent auto-denied dangerous command: %s (%s). "
76
+ "Set delegation.subagent_auto_approve: true to allow.",
77
+ command, description,
78
+ )
79
+ return "deny"
80
+
81
+
82
+ def _subagent_auto_approve(command: str, description: str, **kwargs) -> str:
83
+ """Auto-approve dangerous commands in subagent threads (opt-in YOLO).
84
+
85
+ Only installed when delegation.subagent_auto_approve=true. Returns 'once'
86
+ so the subagent proceeds without blocking the parent UI.
87
+ """
88
+ logger.warning(
89
+ "Subagent auto-approved dangerous command: %s (%s)",
90
+ command, description,
91
+ )
92
+ return "once"
93
+
94
+
95
+ def _get_subagent_approval_callback():
96
+ """Return the callback to install into subagent worker threads.
97
+
98
+ Config key: delegation.subagent_auto_approve (bool, default False).
99
+ Reads via the same _load_config() path as the rest of delegate_task so
100
+ priority is config.yaml > (no env override for this knob) > default.
101
+ """
102
+ cfg = _load_config()
103
+ val = cfg.get("subagent_auto_approve", False)
104
+ if is_truthy_value(val):
105
+ return _subagent_auto_approve
106
+ return _subagent_auto_deny
107
+
108
+ # Build a description fragment listing toolsets available for subagents.
109
+ # Excludes toolsets where ALL tools are blocked, composite/platform toolsets
110
+ # (hermes-* prefixed), and scenario toolsets.
111
+ #
112
+ # NOTE: "delegation" is in this exclusion set so the subagent-facing
113
+ # capability hint string (_TOOLSET_LIST_STR) doesn't advertise it as a
114
+ # toolset to request explicitly — the correct mechanism for nested
115
+ # delegation is role='orchestrator', which re-adds "delegation" in
116
+ # _build_child_agent regardless of this exclusion.
117
+ _EXCLUDED_TOOLSET_NAMES = frozenset({"debugging", "safe", "delegation", "moa", "rl"})
118
+ _SUBAGENT_TOOLSETS = sorted(
119
+ name
120
+ for name, defn in TOOLSETS.items()
121
+ if name not in _EXCLUDED_TOOLSET_NAMES
122
+ and not name.startswith("hermes-")
123
+ and not all(t in DELEGATE_BLOCKED_TOOLS for t in defn.get("tools", []))
124
+ )
125
+ _TOOLSET_LIST_STR = ", ".join(f"'{n}'" for n in _SUBAGENT_TOOLSETS)
126
+
127
+ _DEFAULT_MAX_CONCURRENT_CHILDREN = 3
128
+ MAX_DEPTH = 1 # flat by default: parent (0) -> child (1); grandchild rejected unless max_spawn_depth raised.
129
+ # Configurable depth cap consulted by _get_max_spawn_depth; MAX_DEPTH
130
+ # stays as the default fallback and is still the symbol tests import.
131
+ _MIN_SPAWN_DEPTH = 1
132
+ _MAX_SPAWN_DEPTH_CAP = 3
133
+
134
+
135
+ # ---------------------------------------------------------------------------
136
+ # Runtime state: pause flag + active subagent registry
137
+ #
138
+ # Consumed by the TUI observability layer (overlay/control surface) and the
139
+ # gateway RPCs `delegation.pause`, `delegation.status`, `subagent.interrupt`.
140
+ # Kept module-level so they span every delegate_task invocation in the
141
+ # process, including nested orchestrator -> worker chains.
142
+ # ---------------------------------------------------------------------------
143
+
144
+ _spawn_pause_lock = threading.Lock()
145
+ _spawn_paused: bool = False
146
+
147
+ _active_subagents_lock = threading.Lock()
148
+ # subagent_id -> mutable record tracking the live child agent. Stays only
149
+ # for the lifetime of the run; _run_single_child is the owner.
150
+ _active_subagents: Dict[str, Dict[str, Any]] = {}
151
+
152
+
153
+ def set_spawn_paused(paused: bool) -> bool:
154
+ """Globally block/unblock new delegate_task spawns.
155
+
156
+ Active children keep running; only NEW calls to delegate_task fail fast
157
+ with a "spawning paused" error until unblocked. Returns the new state.
158
+ """
159
+ global _spawn_paused
160
+ with _spawn_pause_lock:
161
+ _spawn_paused = bool(paused)
162
+ return _spawn_paused
163
+
164
+
165
+ def is_spawn_paused() -> bool:
166
+ with _spawn_pause_lock:
167
+ return _spawn_paused
168
+
169
+
170
+ def _register_subagent(record: Dict[str, Any]) -> None:
171
+ sid = record.get("subagent_id")
172
+ if not sid:
173
+ return
174
+ with _active_subagents_lock:
175
+ _active_subagents[sid] = record
176
+
177
+
178
+ def _unregister_subagent(subagent_id: str) -> None:
179
+ with _active_subagents_lock:
180
+ _active_subagents.pop(subagent_id, None)
181
+
182
+
183
+ def interrupt_subagent(subagent_id: str) -> bool:
184
+ """Request that a single running subagent stop at its next iteration boundary.
185
+
186
+ Does not hard-kill the worker thread (Python can't); sets the child's
187
+ interrupt flag which propagates to in-flight tools and recurses into
188
+ grandchildren via AIAgent.interrupt(). Returns True if a matching
189
+ subagent was found.
190
+ """
191
+ with _active_subagents_lock:
192
+ record = _active_subagents.get(subagent_id)
193
+ if not record:
194
+ return False
195
+ agent = record.get("agent")
196
+ if agent is None:
197
+ return False
198
+ try:
199
+ agent.interrupt(f"Interrupted via TUI ({subagent_id})")
200
+ except Exception as exc:
201
+ logger.debug("interrupt_subagent(%s) failed: %s", subagent_id, exc)
202
+ return False
203
+ return True
204
+
205
+
206
+ def list_active_subagents() -> List[Dict[str, Any]]:
207
+ """Snapshot of the currently running subagent tree.
208
+
209
+ Each record: {subagent_id, parent_id, depth, goal, model, started_at,
210
+ tool_count, status}. Safe to call from any thread — returns a copy.
211
+ """
212
+ with _active_subagents_lock:
213
+ return [
214
+ {k: v for k, v in r.items() if k != "agent"}
215
+ for r in _active_subagents.values()
216
+ ]
217
+
218
+
219
+ def _extract_output_tail(
220
+ result: Dict[str, Any],
221
+ *,
222
+ max_entries: int = 12,
223
+ max_chars: int = 8000,
224
+ ) -> List[Dict[str, Any]]:
225
+ """Pull the last N tool-call results from a child's conversation.
226
+
227
+ Powers the overlay's "Output" section — the cc-swarm-parity feature.
228
+ We reuse the same messages list the trajectory saver walks, taking
229
+ only the tail to keep event payloads small. Each entry is
230
+ ``{tool, preview, is_error}``.
231
+ """
232
+ messages = result.get("messages") if isinstance(result, dict) else None
233
+ if not isinstance(messages, list):
234
+ return []
235
+
236
+ # Walk in reverse to build a tail; stop when we have enough.
237
+ tail: List[Dict[str, Any]] = []
238
+ pending_call_by_id: Dict[str, str] = {}
239
+
240
+ # First pass (forward): build tool_call_id -> tool_name map
241
+ for msg in messages:
242
+ if not isinstance(msg, dict):
243
+ continue
244
+ if msg.get("role") == "assistant":
245
+ for tc in msg.get("tool_calls") or []:
246
+ tc_id = tc.get("id")
247
+ fn = tc.get("function") or {}
248
+ if tc_id:
249
+ pending_call_by_id[tc_id] = str(fn.get("name") or "tool")
250
+
251
+ # Second pass (reverse): pick tool results, newest first
252
+ for msg in reversed(messages):
253
+ if len(tail) >= max_entries:
254
+ break
255
+ if not isinstance(msg, dict) or msg.get("role") != "tool":
256
+ continue
257
+ content = msg.get("content") or ""
258
+ if not isinstance(content, str):
259
+ content = str(content)
260
+ is_error = _looks_like_error_output(content)
261
+ tool_name = pending_call_by_id.get(msg.get("tool_call_id") or "", "tool")
262
+ # Preserve line structure so the overlay's wrapped scroll region can
263
+ # show real output rather than a whitespace-collapsed blob. We still
264
+ # cap the payload size to keep events bounded.
265
+ preview = content[:max_chars]
266
+ tail.append({"tool": tool_name, "preview": preview, "is_error": is_error})
267
+
268
+ tail.reverse() # restore chronological order for display
269
+ return tail
270
+
271
+
272
+ def _looks_like_error_output(content: str) -> bool:
273
+ """Conservative stderr/error detector for tool-result previews.
274
+
275
+ The old heuristic flagged any preview containing the substring "error",
276
+ which painted perfectly normal terminal/json output red. We now only
277
+ mark output as an error when there is stronger evidence:
278
+ - structured JSON with an ``error`` key
279
+ - structured JSON with ``status`` of error/failed
280
+ - first line starts with a classic error marker
281
+ """
282
+ if not content:
283
+ return False
284
+
285
+ head = content.lstrip()
286
+ if head.startswith("{") or head.startswith("["):
287
+ try:
288
+ parsed = json.loads(content)
289
+ if isinstance(parsed, dict):
290
+ if parsed.get("error"):
291
+ return True
292
+ status = str(parsed.get("status") or "").strip().lower()
293
+ if status in {"error", "failed", "failure", "timeout"}:
294
+ return True
295
+ except Exception:
296
+ pass
297
+
298
+ first = content.splitlines()[0].strip().lower() if content.splitlines() else ""
299
+ return (
300
+ first.startswith("error:")
301
+ or first.startswith("failed:")
302
+ or first.startswith("traceback ")
303
+ or first.startswith("exception:")
304
+ )
305
+
306
+
307
+ def _normalize_role(r: Optional[str]) -> str:
308
+ """Normalise a caller-provided role to 'leaf' or 'orchestrator'.
309
+
310
+ None/empty -> 'leaf'. Unknown strings coerce to 'leaf' with a
311
+ warning log (matches the silent-degrade pattern of
312
+ _get_orchestrator_enabled). _build_child_agent adds a second
313
+ degrade layer for depth/kill-switch bounds.
314
+ """
315
+ if r is None or not r:
316
+ return "leaf"
317
+ r_norm = str(r).strip().lower()
318
+ if r_norm in {"leaf", "orchestrator"}:
319
+ return r_norm
320
+ logger.warning("Unknown delegate_task role=%r, coercing to 'leaf'", r)
321
+ return "leaf"
322
+
323
+
324
+ def _get_max_concurrent_children() -> int:
325
+ """Read delegation.max_concurrent_children from config, falling back to
326
+ DELEGATION_MAX_CONCURRENT_CHILDREN env var, then the default (3).
327
+
328
+ Users can raise this as high as they want; only the floor (1) is enforced.
329
+
330
+ Uses the same ``_load_config()`` path that the rest of ``delegate_task``
331
+ uses, keeping config priority consistent (config.yaml > env > default).
332
+ """
333
+ cfg = _load_config()
334
+ val = cfg.get("max_concurrent_children")
335
+ if val is not None:
336
+ try:
337
+ result = max(1, int(val))
338
+ if result > 10:
339
+ logger.warning(
340
+ "delegation.max_concurrent_children=%d: each child consumes API tokens "
341
+ "independently. High values multiply cost linearly.",
342
+ result,
343
+ )
344
+ return result
345
+ except (TypeError, ValueError):
346
+ logger.warning(
347
+ "delegation.max_concurrent_children=%r is not a valid integer; "
348
+ "using default %d",
349
+ val,
350
+ _DEFAULT_MAX_CONCURRENT_CHILDREN,
351
+ )
352
+ return _DEFAULT_MAX_CONCURRENT_CHILDREN
353
+ env_val = os.getenv("DELEGATION_MAX_CONCURRENT_CHILDREN")
354
+ if env_val:
355
+ try:
356
+ return max(1, int(env_val))
357
+ except (TypeError, ValueError):
358
+ return _DEFAULT_MAX_CONCURRENT_CHILDREN
359
+ return _DEFAULT_MAX_CONCURRENT_CHILDREN
360
+
361
+
362
+ def _get_child_timeout() -> float:
363
+ """Read delegation.child_timeout_seconds from config.
364
+
365
+ Returns the number of seconds a single child agent is allowed to run
366
+ before being considered stuck. Default: 600 s (10 minutes).
367
+ """
368
+ cfg = _load_config()
369
+ val = cfg.get("child_timeout_seconds")
370
+ if val is not None:
371
+ try:
372
+ return max(30.0, float(val))
373
+ except (TypeError, ValueError):
374
+ logger.warning(
375
+ "delegation.child_timeout_seconds=%r is not a valid number; "
376
+ "using default %d",
377
+ val,
378
+ DEFAULT_CHILD_TIMEOUT,
379
+ )
380
+ env_val = os.getenv("DELEGATION_CHILD_TIMEOUT_SECONDS")
381
+ if env_val:
382
+ try:
383
+ return max(30.0, float(env_val))
384
+ except (TypeError, ValueError):
385
+ pass
386
+ return float(DEFAULT_CHILD_TIMEOUT)
387
+
388
+
389
+ def _get_max_spawn_depth() -> int:
390
+ """Read delegation.max_spawn_depth from config, clamped to [1, 3].
391
+
392
+ depth 0 = parent agent. max_spawn_depth = N means agents at depths
393
+ 0..N-1 can spawn; depth N is the leaf floor. Default 1 is flat:
394
+ parent spawns children (depth 1), depth-1 children cannot spawn
395
+ (blocked by this guard AND, for leaf children, by the delegation
396
+ toolset strip in _strip_blocked_tools).
397
+
398
+ Raise to 2 or 3 to unlock nested orchestration. role="orchestrator"
399
+ removes the toolset strip for depth-1 children when
400
+ max_spawn_depth >= 2, enabling them to spawn their own workers.
401
+ """
402
+ cfg = _load_config()
403
+ val = cfg.get("max_spawn_depth")
404
+ if val is None:
405
+ return MAX_DEPTH
406
+ try:
407
+ ival = int(val)
408
+ except (TypeError, ValueError):
409
+ logger.warning(
410
+ "delegation.max_spawn_depth=%r is not a valid integer; " "using default %d",
411
+ val,
412
+ MAX_DEPTH,
413
+ )
414
+ return MAX_DEPTH
415
+ clamped = max(_MIN_SPAWN_DEPTH, min(_MAX_SPAWN_DEPTH_CAP, ival))
416
+ if clamped != ival:
417
+ logger.warning(
418
+ "delegation.max_spawn_depth=%d out of range [%d, %d]; " "clamping to %d",
419
+ ival,
420
+ _MIN_SPAWN_DEPTH,
421
+ _MAX_SPAWN_DEPTH_CAP,
422
+ clamped,
423
+ )
424
+ return clamped
425
+
426
+
427
+ def _get_orchestrator_enabled() -> bool:
428
+ """Global kill switch for the orchestrator role.
429
+
430
+ When False, role="orchestrator" is silently forced to "leaf" in
431
+ _build_child_agent and the delegation toolset is stripped as before.
432
+ Lets an operator disable the feature without a code revert.
433
+ """
434
+ cfg = _load_config()
435
+ val = cfg.get("orchestrator_enabled", True)
436
+ if isinstance(val, bool):
437
+ return val
438
+ # Accept "true"/"false" strings from YAML that doesn't auto-coerce.
439
+ if isinstance(val, str):
440
+ return val.strip().lower() in {"true", "1", "yes", "on"}
441
+ return True
442
+
443
+
444
+ def _get_inherit_mcp_toolsets() -> bool:
445
+ """Whether narrowed child toolsets should keep the parent's MCP toolsets."""
446
+ cfg = _load_config()
447
+ return is_truthy_value(cfg.get("inherit_mcp_toolsets"), default=True)
448
+
449
+
450
+ def _is_mcp_toolset_name(name: str) -> bool:
451
+ """Return True for canonical MCP toolsets and their registered aliases."""
452
+ if not name:
453
+ return False
454
+ if str(name).startswith("mcp-"):
455
+ return True
456
+ try:
457
+ from tools.registry import registry
458
+
459
+ target = registry.get_toolset_alias_target(str(name))
460
+ except Exception:
461
+ target = None
462
+ return bool(target and str(target).startswith("mcp-"))
463
+
464
+
465
+ def _expand_parent_toolsets(parent_toolsets: set) -> set:
466
+ """Expand composite toolsets so individual toolset names are recognized.
467
+
468
+ When a parent uses a composite toolset like ``hermes-cli`` (which bundles
469
+ all core tools), the child may request individual toolsets such as ``web``
470
+ or ``terminal``. A simple name-based intersection would reject them
471
+ because ``"web" != "hermes-cli"``.
472
+
473
+ This helper collects the tool names from each parent toolset, then adds
474
+ the names of any individual toolsets whose tools are a *subset* of the
475
+ parent's available tools. The original parent toolset names are preserved.
476
+ """
477
+ parent_tool_names: set = set()
478
+ for ts_name in parent_toolsets:
479
+ ts_def = TOOLSETS.get(ts_name)
480
+ if ts_def:
481
+ parent_tool_names.update(ts_def.get("tools", []))
482
+
483
+ if not parent_tool_names:
484
+ return set(parent_toolsets)
485
+
486
+ expanded = set(parent_toolsets)
487
+ for ts_name, ts_def in TOOLSETS.items():
488
+ if ts_name in expanded:
489
+ continue
490
+ ts_tools = ts_def.get("tools", [])
491
+ if ts_tools and set(ts_tools).issubset(parent_tool_names):
492
+ expanded.add(ts_name)
493
+ return expanded
494
+
495
+
496
+ def _preserve_parent_mcp_toolsets(
497
+ child_toolsets: List[str], parent_toolsets: set[str]
498
+ ) -> List[str]:
499
+ """Append any parent MCP toolsets that are missing from a narrowed child."""
500
+ preserved = list(child_toolsets)
501
+ for toolset_name in sorted(parent_toolsets):
502
+ if _is_mcp_toolset_name(toolset_name) and toolset_name not in preserved:
503
+ preserved.append(toolset_name)
504
+ return preserved
505
+
506
+
507
+ DEFAULT_MAX_ITERATIONS = 50
508
+ DEFAULT_CHILD_TIMEOUT = 600 # seconds before a child agent is considered stuck
509
+ _HEARTBEAT_INTERVAL = 30 # seconds between parent activity heartbeats during delegation
510
+ # Stale-heartbeat thresholds. A child with no API-call progress is either:
511
+ # - idle between turns (no current_tool) — probably stuck on a slow API call
512
+ # - inside a tool (current_tool set) — probably running a legitimately long
513
+ # operation (terminal command, web fetch, large file read)
514
+ # The idle ceiling stays tight so genuinely stuck children don't mask the gateway
515
+ # timeout. The in-tool ceiling is much higher so legit long-running tools get
516
+ # time to finish; child_timeout_seconds (default 600s) is still the hard cap.
517
+ _HEARTBEAT_STALE_CYCLES_IDLE = 15 # 15 * 30s = 450s idle between turns → stale
518
+ _HEARTBEAT_STALE_CYCLES_IN_TOOL = 40 # 40 * 30s = 1200s stuck on same tool → stale
519
+ DEFAULT_TOOLSETS = ["terminal", "file", "web"]
520
+
521
+
522
+ # ---------------------------------------------------------------------------
523
+ # Delegation progress event types
524
+ # ---------------------------------------------------------------------------
525
+
526
+
527
+ class DelegateEvent(str, enum.Enum):
528
+ """Formal event types emitted during delegation progress.
529
+
530
+ _build_child_progress_callback normalises incoming legacy strings
531
+ (``tool.started``, ``_thinking``, …) to these enum values via
532
+ ``_LEGACY_EVENT_MAP``. External consumers (gateway SSE, ACP adapter,
533
+ CLI) still receive the legacy strings during the deprecation window.
534
+
535
+ TASK_SPAWNED / TASK_COMPLETED / TASK_FAILED are reserved for
536
+ future orchestrator lifecycle events and are not currently emitted.
537
+ """
538
+
539
+ TASK_SPAWNED = "delegate.task_spawned"
540
+ TASK_PROGRESS = "delegate.task_progress"
541
+ TASK_COMPLETED = "delegate.task_completed"
542
+ TASK_FAILED = "delegate.task_failed"
543
+ TASK_THINKING = "delegate.task_thinking"
544
+ TASK_TOOL_STARTED = "delegate.tool_started"
545
+ TASK_TOOL_COMPLETED = "delegate.tool_completed"
546
+
547
+
548
+ # Legacy event strings → DelegateEvent mapping.
549
+ # Incoming child-agent events use the old names; the callback normalises them.
550
+ _LEGACY_EVENT_MAP: Dict[str, DelegateEvent] = {
551
+ "_thinking": DelegateEvent.TASK_THINKING,
552
+ "reasoning.available": DelegateEvent.TASK_THINKING,
553
+ "tool.started": DelegateEvent.TASK_TOOL_STARTED,
554
+ "tool.completed": DelegateEvent.TASK_TOOL_COMPLETED,
555
+ "subagent_progress": DelegateEvent.TASK_PROGRESS,
556
+ }
557
+
558
+
559
+ def check_delegate_requirements() -> bool:
560
+ """Delegation has no external requirements -- always available."""
561
+ return True
562
+
563
+
564
+ def _build_child_system_prompt(
565
+ goal: str,
566
+ context: Optional[str] = None,
567
+ *,
568
+ workspace_path: Optional[str] = None,
569
+ role: str = "leaf",
570
+ max_spawn_depth: int = 2,
571
+ child_depth: int = 1,
572
+ ) -> str:
573
+ """Build a focused system prompt for a child agent.
574
+
575
+ When role='orchestrator', appends a delegation-capability block
576
+ modeled on OpenClaw's buildSubagentSystemPrompt (canSpawn branch at
577
+ inspiration/openclaw/src/agents/subagent-system-prompt.ts:63-95).
578
+ The depth note is literal truth (grounded in the passed config) so
579
+ the LLM doesn't confabulate nesting capabilities that don't exist.
580
+ """
581
+ parts = [
582
+ "You are a focused subagent working on a specific delegated task.",
583
+ "",
584
+ f"YOUR TASK:\n{goal}",
585
+ ]
586
+ if context and context.strip():
587
+ parts.append(f"\nCONTEXT:\n{context}")
588
+ if workspace_path and str(workspace_path).strip():
589
+ parts.append(
590
+ "\nWORKSPACE PATH:\n"
591
+ f"{workspace_path}\n"
592
+ "Use this exact path for local repository/workdir operations unless the task explicitly says otherwise."
593
+ )
594
+ parts.append(
595
+ "\nComplete this task using the tools available to you. "
596
+ "When finished, provide a clear, concise summary of:\n"
597
+ "- What you did\n"
598
+ "- What you found or accomplished\n"
599
+ "- Any files you created or modified\n"
600
+ "- Any issues encountered\n\n"
601
+ "Important workspace rule: Never assume a repository lives at /workspace/... or any other container-style path unless the task/context explicitly gives that path. "
602
+ "If no exact local path is provided, discover it first before issuing git/workdir-specific commands.\n\n"
603
+ "Be thorough but concise -- your response is returned to the "
604
+ "parent agent as a summary."
605
+ )
606
+ if role == "orchestrator":
607
+ child_note = (
608
+ "Your own children MUST be leaves (cannot delegate further) "
609
+ "because they would be at the depth floor — you cannot pass "
610
+ "role='orchestrator' to your own delegate_task calls."
611
+ if child_depth + 1 >= max_spawn_depth
612
+ else "Your own children can themselves be orchestrators or leaves, "
613
+ "depending on the `role` you pass to delegate_task. Default is "
614
+ "'leaf'; pass role='orchestrator' explicitly when a child "
615
+ "needs to further decompose its work."
616
+ )
617
+ parts.append(
618
+ "\n## Subagent Spawning (Orchestrator Role)\n"
619
+ "You have access to the `delegate_task` tool and CAN spawn "
620
+ "your own subagents to parallelize independent work.\n\n"
621
+ "WHEN to delegate:\n"
622
+ "- The goal decomposes into 2+ independent subtasks that can "
623
+ "run in parallel (e.g. research A and B simultaneously).\n"
624
+ "- A subtask is reasoning-heavy and would flood your context "
625
+ "with intermediate data.\n\n"
626
+ "WHEN NOT to delegate:\n"
627
+ "- Single-step mechanical work — do it directly.\n"
628
+ "- Trivial tasks you can execute in one or two tool calls.\n"
629
+ "- Re-delegating your entire assigned goal to one worker "
630
+ "(that's just pass-through with no value added).\n\n"
631
+ "Coordinate your workers' results and synthesize them before "
632
+ "reporting back to your parent. You are responsible for the "
633
+ "final summary, not your workers.\n\n"
634
+ f"NOTE: You are at depth {child_depth}. The delegation tree "
635
+ f"is capped at max_spawn_depth={max_spawn_depth}. {child_note}"
636
+ )
637
+ return "\n".join(parts)
638
+
639
+
640
+ def _resolve_workspace_hint(parent_agent) -> Optional[str]:
641
+ """Best-effort local workspace hint for child prompts.
642
+
643
+ We only inject a path when we have a concrete absolute directory. This avoids
644
+ teaching subagents a fake container path while still helping them avoid
645
+ guessing `/workspace/...` for local repo tasks.
646
+ """
647
+ candidates = [
648
+ os.getenv("TERMINAL_CWD"),
649
+ getattr(
650
+ getattr(parent_agent, "_subdirectory_hints", None), "working_dir", None
651
+ ),
652
+ getattr(parent_agent, "terminal_cwd", None),
653
+ getattr(parent_agent, "cwd", None),
654
+ ]
655
+ for candidate in candidates:
656
+ if not candidate:
657
+ continue
658
+ try:
659
+ text = os.path.abspath(os.path.expanduser(str(candidate)))
660
+ except Exception:
661
+ continue
662
+ if os.path.isabs(text) and os.path.isdir(text):
663
+ return text
664
+ return None
665
+
666
+
667
+ def _strip_blocked_tools(toolsets: List[str]) -> List[str]:
668
+ """Remove toolsets that contain only blocked tools."""
669
+ blocked_toolset_names = {
670
+ "delegation",
671
+ "clarify",
672
+ "memory",
673
+ "code_execution",
674
+ }
675
+ return [t for t in toolsets if t not in blocked_toolset_names]
676
+
677
+
678
+ def _build_child_progress_callback(
679
+ task_index: int,
680
+ goal: str,
681
+ parent_agent,
682
+ task_count: int = 1,
683
+ *,
684
+ subagent_id: Optional[str] = None,
685
+ parent_id: Optional[str] = None,
686
+ depth: Optional[int] = None,
687
+ model: Optional[str] = None,
688
+ toolsets: Optional[List[str]] = None,
689
+ ) -> Optional[callable]:
690
+ """Build a callback that relays child agent tool calls to the parent display.
691
+
692
+ Two display paths:
693
+ CLI: prints tree-view lines above the parent's delegation spinner
694
+ Gateway: batches tool names and relays to parent's progress callback
695
+
696
+ The identity kwargs (``subagent_id``, ``parent_id``, ``depth``, ``model``,
697
+ ``toolsets``) are threaded into every relayed event so the TUI can
698
+ reconstruct the live spawn tree and route per-branch controls (kill,
699
+ pause) back by ``subagent_id``. All are optional for backward compat —
700
+ older callers that ignore them still produce a flat list on the TUI.
701
+
702
+ Returns None if no display mechanism is available, in which case the
703
+ child agent runs with no progress callback (identical to current behavior).
704
+ """
705
+ spinner = getattr(parent_agent, "_delegate_spinner", None)
706
+ parent_cb = getattr(parent_agent, "tool_progress_callback", None)
707
+
708
+ if not spinner and not parent_cb:
709
+ return None # No display → no callback → zero behavior change
710
+
711
+ # Show 1-indexed prefix only in batch mode (multiple tasks)
712
+ prefix = f"[{task_index + 1}] " if task_count > 1 else ""
713
+ goal_label = (goal or "").strip()
714
+
715
+ # Gateway: batch tool names, flush periodically
716
+ _BATCH_SIZE = 5
717
+ _batch: List[str] = []
718
+ _tool_count = [0] # per-subagent running counter (list for closure mutation)
719
+
720
+ def _identity_kwargs() -> Dict[str, Any]:
721
+ kw: Dict[str, Any] = {
722
+ "task_index": task_index,
723
+ "task_count": task_count,
724
+ "goal": goal_label,
725
+ }
726
+ if subagent_id is not None:
727
+ kw["subagent_id"] = subagent_id
728
+ if parent_id is not None:
729
+ kw["parent_id"] = parent_id
730
+ if depth is not None:
731
+ kw["depth"] = depth
732
+ if model is not None:
733
+ kw["model"] = model
734
+ if toolsets is not None:
735
+ kw["toolsets"] = list(toolsets)
736
+ kw["tool_count"] = _tool_count[0]
737
+ return kw
738
+
739
+ def _relay(
740
+ event_type: str, tool_name: str = None, preview: str = None, args=None, **kwargs
741
+ ):
742
+ if not parent_cb:
743
+ return
744
+ payload = _identity_kwargs()
745
+ payload.update(kwargs) # caller overrides (e.g. status, duration_seconds)
746
+ try:
747
+ parent_cb(event_type, tool_name, preview, args, **payload)
748
+ except Exception as e:
749
+ logger.debug("Parent callback failed: %s", e)
750
+
751
+ def _callback(
752
+ event_type, tool_name: str = None, preview: str = None, args=None, **kwargs
753
+ ):
754
+ # Lifecycle events emitted by the orchestrator itself — handled
755
+ # before enum normalisation since they are not part of DelegateEvent.
756
+ if event_type == "subagent.start":
757
+ if spinner and goal_label:
758
+ short = (
759
+ (goal_label[:55] + "...") if len(goal_label) > 55 else goal_label
760
+ )
761
+ try:
762
+ spinner.print_above(f" {prefix}├─ 🔀 {short}")
763
+ except Exception as e:
764
+ logger.debug("Spinner print_above failed: %s", e)
765
+ _relay("subagent.start", preview=preview or goal_label or "", **kwargs)
766
+ return
767
+
768
+ if event_type == "subagent.complete":
769
+ _relay("subagent.complete", preview=preview, **kwargs)
770
+ return
771
+
772
+ # Normalise legacy strings, new-style "delegate.*" strings, and
773
+ # DelegateEvent enum values all to a single DelegateEvent. The
774
+ # original implementation only accepted the five legacy strings;
775
+ # enum-typed callers were silently dropped.
776
+ if isinstance(event_type, DelegateEvent):
777
+ event = event_type
778
+ else:
779
+ event = _LEGACY_EVENT_MAP.get(event_type)
780
+ if event is None:
781
+ try:
782
+ event = DelegateEvent(event_type)
783
+ except (ValueError, TypeError):
784
+ return # Unknown event — ignore
785
+
786
+ if event == DelegateEvent.TASK_THINKING:
787
+ text = preview or tool_name or ""
788
+ if spinner:
789
+ short = (text[:55] + "...") if len(text) > 55 else text
790
+ try:
791
+ spinner.print_above(f' {prefix}├─ 💭 "{short}"')
792
+ except Exception as e:
793
+ logger.debug("Spinner print_above failed: %s", e)
794
+ _relay("subagent.thinking", preview=text)
795
+ return
796
+
797
+ if event == DelegateEvent.TASK_TOOL_COMPLETED:
798
+ return
799
+
800
+ if event == DelegateEvent.TASK_PROGRESS:
801
+ # Pre-batched progress summary relayed from a nested
802
+ # orchestrator's grandchild (upstream emits as
803
+ # parent_cb("subagent_progress", summary_string) where the
804
+ # summary lands in the tool_name positional slot). Treat as
805
+ # a pass-through: render distinctly (not via the tool-start
806
+ # emoji lookup, which would mistake the summary string for a
807
+ # tool name) and relay upward without re-batching.
808
+ summary_text = tool_name or preview or ""
809
+ if spinner and summary_text:
810
+ try:
811
+ spinner.print_above(f" {prefix}├─ 🔀 {summary_text}")
812
+ except Exception as e:
813
+ logger.debug("Spinner print_above failed: %s", e)
814
+ if parent_cb:
815
+ try:
816
+ parent_cb("subagent_progress", f"{prefix}{summary_text}")
817
+ except Exception as e:
818
+ logger.debug("Parent callback relay failed: %s", e)
819
+ return
820
+
821
+ # TASK_TOOL_STARTED — display and batch for parent relay
822
+ _tool_count[0] += 1
823
+ if subagent_id is not None:
824
+ with _active_subagents_lock:
825
+ rec = _active_subagents.get(subagent_id)
826
+ if rec is not None:
827
+ rec["tool_count"] = _tool_count[0]
828
+ rec["last_tool"] = tool_name or ""
829
+ if spinner:
830
+ short = (
831
+ (preview[:35] + "...")
832
+ if preview and len(preview) > 35
833
+ else (preview or "")
834
+ )
835
+ from agent.display import get_tool_emoji
836
+
837
+ emoji = get_tool_emoji(tool_name or "")
838
+ line = f" {prefix}├─ {emoji} {tool_name}"
839
+ if short:
840
+ line += f' "{short}"'
841
+ try:
842
+ spinner.print_above(line)
843
+ except Exception as e:
844
+ logger.debug("Spinner print_above failed: %s", e)
845
+
846
+ if parent_cb:
847
+ _relay("subagent.tool", tool_name, preview, args)
848
+ _batch.append(tool_name or "")
849
+ if len(_batch) >= _BATCH_SIZE:
850
+ summary = ", ".join(_batch)
851
+ _relay("subagent.progress", preview=f"🔀 {prefix}{summary}")
852
+ _batch.clear()
853
+
854
+ def _flush():
855
+ """Flush remaining batched tool names to gateway on completion."""
856
+ if parent_cb and _batch:
857
+ summary = ", ".join(_batch)
858
+ _relay("subagent.progress", preview=f"🔀 {prefix}{summary}")
859
+ _batch.clear()
860
+
861
+ _callback._flush = _flush
862
+ return _callback
863
+
864
+
865
+ def _build_child_agent(
866
+ task_index: int,
867
+ goal: str,
868
+ context: Optional[str],
869
+ toolsets: Optional[List[str]],
870
+ model: Optional[str],
871
+ max_iterations: int,
872
+ task_count: int,
873
+ parent_agent,
874
+ # Credential overrides from delegation config (provider:model resolution)
875
+ override_provider: Optional[str] = None,
876
+ override_base_url: Optional[str] = None,
877
+ override_api_key: Optional[str] = None,
878
+ override_api_mode: Optional[str] = None,
879
+ # ACP transport overrides — lets a non-ACP parent spawn ACP child agents
880
+ override_acp_command: Optional[str] = None,
881
+ override_acp_args: Optional[List[str]] = None,
882
+ # Per-call role controlling whether the child can further delegate.
883
+ # 'leaf' (default) cannot; 'orchestrator' retains the delegation
884
+ # toolset subject to depth/kill-switch bounds applied below.
885
+ role: str = "leaf",
886
+ ):
887
+ """
888
+ Build a child AIAgent on the main thread (thread-safe construction).
889
+ Returns the constructed child agent without running it.
890
+
891
+ When override_* params are set (from delegation config), the child uses
892
+ those credentials instead of inheriting from the parent. This enables
893
+ routing subagents to a different provider:model pair (e.g. cheap/fast
894
+ model on OpenRouter while the parent runs on Nous Portal).
895
+ """
896
+ from run_agent import AIAgent
897
+ import uuid as _uuid
898
+
899
+ # ── Role resolution ─────────────────────────────────────────────────
900
+ # Honor the caller's role only when BOTH the kill switch and the
901
+ # child's depth allow it. This is the single point where role
902
+ # degrades to 'leaf' — keeps the rule predictable. Callers pass
903
+ # the normalised role (_normalize_role ran in delegate_task) so
904
+ # we only deal with 'leaf' or 'orchestrator' here.
905
+ child_depth = getattr(parent_agent, "_delegate_depth", 0) + 1
906
+ max_spawn = _get_max_spawn_depth()
907
+ orchestrator_ok = _get_orchestrator_enabled() and child_depth < max_spawn
908
+ effective_role = role if (role == "orchestrator" and orchestrator_ok) else "leaf"
909
+
910
+ # ── Subagent identity (stable across events, 0-indexed for TUI) ─────
911
+ # subagent_id is generated here so the progress callback, the
912
+ # spawn_requested event, and the _active_subagents registry all share
913
+ # one key. parent_id is non-None when THIS parent is itself a subagent
914
+ # (nested orchestrator -> worker chain).
915
+ subagent_id = f"sa-{task_index}-{_uuid.uuid4().hex[:8]}"
916
+ parent_subagent_id = getattr(parent_agent, "_subagent_id", None)
917
+ tui_depth = max(0, child_depth - 1) # 0 = first-level child for the UI
918
+
919
+ delegation_cfg = _load_config()
920
+
921
+ # When no explicit toolsets given, inherit from parent's enabled toolsets
922
+ # so disabled tools (e.g. web) don't leak to subagents.
923
+ # Note: enabled_toolsets=None means "all tools enabled" (the default),
924
+ # so we must derive effective toolsets from the parent's loaded tools.
925
+ parent_enabled = getattr(parent_agent, "enabled_toolsets", None)
926
+ if parent_enabled is not None:
927
+ parent_toolsets = set(parent_enabled)
928
+ elif parent_agent and hasattr(parent_agent, "valid_tool_names"):
929
+ # enabled_toolsets is None (all tools) — derive from loaded tool names
930
+ import model_tools
931
+
932
+ parent_toolsets = {
933
+ ts
934
+ for name in parent_agent.valid_tool_names
935
+ if (ts := model_tools.get_toolset_for_tool(name)) is not None
936
+ }
937
+ else:
938
+ parent_toolsets = set(DEFAULT_TOOLSETS)
939
+
940
+ if toolsets:
941
+ # Intersect with parent — subagent must not gain tools the parent lacks.
942
+ # Expand composite toolsets (e.g. hermes-cli) so that individual
943
+ # toolset names (e.g. web, terminal) are recognised during intersection.
944
+ expanded_parent = _expand_parent_toolsets(parent_toolsets)
945
+ child_toolsets = [t for t in toolsets if t in expanded_parent]
946
+ if _get_inherit_mcp_toolsets():
947
+ child_toolsets = _preserve_parent_mcp_toolsets(
948
+ child_toolsets, parent_toolsets
949
+ )
950
+ child_toolsets = _strip_blocked_tools(child_toolsets)
951
+ elif parent_agent and parent_enabled is not None:
952
+ child_toolsets = _strip_blocked_tools(parent_enabled)
953
+ elif parent_toolsets:
954
+ child_toolsets = _strip_blocked_tools(sorted(parent_toolsets))
955
+ else:
956
+ child_toolsets = _strip_blocked_tools(DEFAULT_TOOLSETS)
957
+
958
+ # Orchestrators retain the 'delegation' toolset that _strip_blocked_tools
959
+ # removed. The re-add is unconditional on parent-toolset membership because
960
+ # orchestrator capability is granted by role, not inherited — see the
961
+ # test_intersection_preserves_delegation_bound test for the design rationale.
962
+ if effective_role == "orchestrator" and "delegation" not in child_toolsets:
963
+ child_toolsets.append("delegation")
964
+
965
+ workspace_hint = _resolve_workspace_hint(parent_agent)
966
+ child_prompt = _build_child_system_prompt(
967
+ goal,
968
+ context,
969
+ workspace_path=workspace_hint,
970
+ role=effective_role,
971
+ max_spawn_depth=max_spawn,
972
+ child_depth=child_depth,
973
+ )
974
+ # Extract parent's API key so subagents inherit auth (e.g. Nous Portal).
975
+ parent_api_key = getattr(parent_agent, "api_key", None)
976
+ if (not parent_api_key) and hasattr(parent_agent, "_client_kwargs"):
977
+ parent_api_key = parent_agent._client_kwargs.get("api_key")
978
+
979
+ # Resolve the child's effective model early so it can ride on every event.
980
+ effective_model_for_cb = model or getattr(parent_agent, "model", None)
981
+
982
+ # Build progress callback to relay tool calls to parent display.
983
+ # Identity kwargs thread the subagent_id through every emitted event so the
984
+ # TUI can reconstruct the spawn tree and route per-branch controls.
985
+ child_progress_cb = _build_child_progress_callback(
986
+ task_index,
987
+ goal,
988
+ parent_agent,
989
+ task_count,
990
+ subagent_id=subagent_id,
991
+ parent_id=parent_subagent_id,
992
+ depth=tui_depth,
993
+ model=effective_model_for_cb,
994
+ toolsets=child_toolsets,
995
+ )
996
+
997
+ # Each subagent gets its own iteration budget capped at max_iterations
998
+ # (configurable via delegation.max_iterations, default 50). This means
999
+ # total iterations across parent + subagents can exceed the parent's
1000
+ # max_iterations. The user controls the per-subagent cap in config.yaml.
1001
+
1002
+ child_thinking_cb = None
1003
+ if child_progress_cb:
1004
+
1005
+ def _child_thinking(text: str) -> None:
1006
+ if not text:
1007
+ return
1008
+ try:
1009
+ child_progress_cb("_thinking", text)
1010
+ except Exception as e:
1011
+ logger.debug("Child thinking callback relay failed: %s", e)
1012
+
1013
+ child_thinking_cb = _child_thinking
1014
+
1015
+ # Resolve effective credentials: config override > parent inherit
1016
+ effective_model = model or parent_agent.model
1017
+ effective_provider = override_provider or getattr(parent_agent, "provider", None)
1018
+ effective_base_url = override_base_url or parent_agent.base_url
1019
+ effective_api_key = override_api_key or parent_api_key
1020
+ # Bug #20558 / PR #20563: api_mode must NOT be inherited when the child uses a
1021
+ # different provider than the parent — each provider has its own API surface
1022
+ # (e.g. MiniMax uses anthropic_messages, DeepSeek uses chat_completions).
1023
+ # Inheriting the parent's mode causes 404 errors when the child routes to the
1024
+ # wrong endpoint. Derive the mode from the target provider when it differs.
1025
+ _parent_provider = getattr(parent_agent, "provider", None) or ""
1026
+ if override_api_mode is not None:
1027
+ effective_api_mode = override_api_mode
1028
+ elif effective_provider != _parent_provider:
1029
+ effective_api_mode = None # force re-derivation from provider's defaults
1030
+ else:
1031
+ effective_api_mode = getattr(parent_agent, "api_mode", None)
1032
+ effective_acp_command = override_acp_command or getattr(
1033
+ parent_agent, "acp_command", None
1034
+ )
1035
+ effective_acp_args = list(
1036
+ override_acp_args
1037
+ if override_acp_args is not None
1038
+ else (getattr(parent_agent, "acp_args", []) or [])
1039
+ )
1040
+
1041
+ # When override_provider is set (e.g. delegation.provider: minimax-cn),
1042
+ # the subagent must use direct API calls — not the parent's ACP transport.
1043
+ # Inheriting acp_command unconditionally causes run_agent.py to initialize
1044
+ # CopilotACPClient, bypassing override credentials entirely (issue #16816).
1045
+ if override_provider and not override_acp_command:
1046
+ effective_acp_command = None
1047
+ effective_acp_args = []
1048
+
1049
+ if override_acp_command:
1050
+ # If explicitly forcing an ACP transport override, the provider MUST be copilot-acp
1051
+ # so run_agent.py initializes the CopilotACPClient.
1052
+ effective_provider = "copilot-acp"
1053
+ effective_api_mode = "chat_completions"
1054
+
1055
+ # Resolve reasoning config: delegation override > parent inherit
1056
+ parent_reasoning = getattr(parent_agent, "reasoning_config", None)
1057
+ child_reasoning = parent_reasoning
1058
+ try:
1059
+ delegation_effort = str(delegation_cfg.get("reasoning_effort") or "").strip()
1060
+ if delegation_effort:
1061
+ from calvyn_constants import parse_reasoning_effort
1062
+
1063
+ parsed = parse_reasoning_effort(delegation_effort)
1064
+ if parsed is not None:
1065
+ child_reasoning = parsed
1066
+ else:
1067
+ logger.warning(
1068
+ "Unknown delegation.reasoning_effort '%s', inheriting parent level",
1069
+ delegation_effort,
1070
+ )
1071
+ except Exception as exc:
1072
+ logger.debug("Could not load delegation reasoning_effort: %s", exc)
1073
+
1074
+ # Inherit the parent's fallback provider chain so subagents can recover
1075
+ # from rate-limits and credential exhaustion exactly like the top-level
1076
+ # agent does. _fallback_chain is a list accepted by AIAgent's
1077
+ # fallback_model parameter (which handles both list and dict forms).
1078
+ parent_fallback = getattr(parent_agent, "_fallback_chain", None) or None
1079
+
1080
+ # Inherit the parent's OpenRouter provider-preference filters by default
1081
+ # (so subagents routed to the same provider honour the same routing
1082
+ # constraints). BUT: when `delegation.provider` is set the user is
1083
+ # explicitly asking the child to run on a different provider, and
1084
+ # parent-level OpenRouter filters (e.g. `only=["Anthropic"]`) would
1085
+ # silently force the child back onto the parent's provider. Clear the
1086
+ # filters in that case so the delegated provider is honoured.
1087
+ child_providers_allowed = getattr(parent_agent, "providers_allowed", None)
1088
+ child_providers_ignored = getattr(parent_agent, "providers_ignored", None)
1089
+ child_providers_order = getattr(parent_agent, "providers_order", None)
1090
+ child_provider_sort = getattr(parent_agent, "provider_sort", None)
1091
+ child_openrouter_min_coding_score = getattr(parent_agent, "openrouter_min_coding_score", None)
1092
+ if override_provider:
1093
+ child_providers_allowed = None
1094
+ child_providers_ignored = None
1095
+ child_providers_order = None
1096
+ child_provider_sort = None
1097
+ # Note: openrouter_min_coding_score is model-gated (only emitted on
1098
+ # openrouter/pareto-code), so we keep it inherited even when the
1099
+ # provider is overridden — it's a no-op on any other model.
1100
+
1101
+ child = AIAgent(
1102
+ base_url=effective_base_url,
1103
+ api_key=effective_api_key,
1104
+ model=effective_model,
1105
+ provider=effective_provider,
1106
+ api_mode=effective_api_mode,
1107
+ acp_command=effective_acp_command,
1108
+ acp_args=effective_acp_args,
1109
+ max_iterations=max_iterations,
1110
+ max_tokens=getattr(parent_agent, "max_tokens", None),
1111
+ reasoning_config=child_reasoning,
1112
+ prefill_messages=getattr(parent_agent, "prefill_messages", None),
1113
+ fallback_model=parent_fallback,
1114
+ enabled_toolsets=child_toolsets,
1115
+ quiet_mode=True,
1116
+ ephemeral_system_prompt=child_prompt,
1117
+ log_prefix=f"[subagent-{task_index}]",
1118
+ platform=parent_agent.platform,
1119
+ skip_context_files=True,
1120
+ skip_memory=True,
1121
+ clarify_callback=None,
1122
+ thinking_callback=child_thinking_cb,
1123
+ session_db=getattr(parent_agent, "_session_db", None),
1124
+ parent_session_id=getattr(parent_agent, "session_id", None),
1125
+ providers_allowed=child_providers_allowed,
1126
+ providers_ignored=child_providers_ignored,
1127
+ providers_order=child_providers_order,
1128
+ provider_sort=child_provider_sort,
1129
+ openrouter_min_coding_score=child_openrouter_min_coding_score,
1130
+ tool_progress_callback=child_progress_cb,
1131
+ iteration_budget=None, # fresh budget per subagent
1132
+ )
1133
+ child._print_fn = getattr(parent_agent, "_print_fn", None)
1134
+ # Set delegation depth so children can't spawn grandchildren
1135
+ child._delegate_depth = child_depth
1136
+ # Stash the post-degrade role for introspection (leaf if the
1137
+ # kill switch or depth bounded the caller's requested role).
1138
+ child._delegate_role = effective_role
1139
+ # Stash subagent identity for nested-delegation event propagation and
1140
+ # for _run_single_child / interrupt_subagent to look up by id.
1141
+ child._subagent_id = subagent_id
1142
+ child._parent_subagent_id = parent_subagent_id
1143
+ child._subagent_goal = goal
1144
+
1145
+ # Share a credential pool with the child when possible so subagents can
1146
+ # rotate credentials on rate limits instead of getting pinned to one key.
1147
+ child_pool = _resolve_child_credential_pool(effective_provider, parent_agent)
1148
+ if child_pool is not None:
1149
+ child._credential_pool = child_pool
1150
+
1151
+ # Register child for interrupt propagation
1152
+ if hasattr(parent_agent, "_active_children"):
1153
+ lock = getattr(parent_agent, "_active_children_lock", None)
1154
+ if lock:
1155
+ with lock:
1156
+ parent_agent._active_children.append(child)
1157
+ else:
1158
+ parent_agent._active_children.append(child)
1159
+
1160
+ # Announce the spawn immediately — the child may sit in a queue
1161
+ # for seconds if max_concurrent_children is saturated, so the TUI
1162
+ # wants a node in the tree before run starts.
1163
+ if child_progress_cb:
1164
+ try:
1165
+ child_progress_cb("subagent.spawn_requested", preview=goal)
1166
+ except Exception as exc:
1167
+ logger.debug("spawn_requested relay failed: %s", exc)
1168
+
1169
+ return child
1170
+
1171
+
1172
+ def _dump_subagent_timeout_diagnostic(
1173
+ *,
1174
+ child: Any,
1175
+ task_index: int,
1176
+ timeout_seconds: float,
1177
+ duration_seconds: float,
1178
+ worker_thread: Optional[threading.Thread],
1179
+ goal: str,
1180
+ ) -> Optional[str]:
1181
+ """Write a structured diagnostic dump for a subagent that timed out
1182
+ before making any API call.
1183
+
1184
+ See issue #14726: users hit "subagent timed out after 300s with no response"
1185
+ with zero API calls and no way to inspect what happened. This helper
1186
+ writes a dedicated log under ``~/.hermes/logs/subagent-<sid>-<ts>.log``
1187
+ capturing the child's config, system-prompt / tool-schema sizes, activity
1188
+ tracker snapshot, and the worker thread's Python stack at timeout.
1189
+
1190
+ Returns the absolute path to the diagnostic file, or None on failure.
1191
+ """
1192
+ try:
1193
+ from calvyn_constants import get_hermes_home
1194
+ import datetime as _dt
1195
+ import sys as _sys
1196
+ import traceback as _traceback
1197
+
1198
+ hermes_home = get_hermes_home()
1199
+ logs_dir = hermes_home / "logs"
1200
+ try:
1201
+ logs_dir.mkdir(parents=True, exist_ok=True)
1202
+ except Exception:
1203
+ return None
1204
+
1205
+ subagent_id = getattr(child, "_subagent_id", None) or f"idx{task_index}"
1206
+ ts = _dt.datetime.now().strftime("%Y%m%d_%H%M%S")
1207
+ dump_path = logs_dir / f"subagent-timeout-{subagent_id}-{ts}.log"
1208
+
1209
+ lines: List[str] = []
1210
+ def _w(line: str = "") -> None:
1211
+ lines.append(line)
1212
+
1213
+ _w(f"# Subagent timeout diagnostic — issue #14726")
1214
+ _w(f"# Generated: {_dt.datetime.now().isoformat()}")
1215
+ _w("")
1216
+ _w("## Timeout")
1217
+ _w(f" task_index: {task_index}")
1218
+ _w(f" subagent_id: {subagent_id}")
1219
+ _w(f" configured_timeout: {timeout_seconds}s")
1220
+ _w(f" actual_duration: {duration_seconds:.2f}s")
1221
+ _w("")
1222
+
1223
+ _w("## Goal")
1224
+ _goal_preview = (goal or "").strip()
1225
+ if len(_goal_preview) > 1000:
1226
+ _goal_preview = _goal_preview[:1000] + " ...[truncated]"
1227
+ _w(_goal_preview or "(empty)")
1228
+ _w("")
1229
+
1230
+ _w("## Child config")
1231
+ for attr in (
1232
+ "model", "provider", "api_mode", "base_url", "max_iterations",
1233
+ "quiet_mode", "skip_memory", "skip_context_files", "platform",
1234
+ "_delegate_role", "_delegate_depth",
1235
+ ):
1236
+ try:
1237
+ val = getattr(child, attr, None)
1238
+ # Redact api_key-shaped values defensively
1239
+ if isinstance(val, str) and attr == "base_url":
1240
+ pass
1241
+ _w(f" {attr}: {val!r}")
1242
+ except Exception:
1243
+ _w(f" {attr}: <unreadable>")
1244
+ _w("")
1245
+
1246
+ _w("## Toolsets")
1247
+ enabled = getattr(child, "enabled_toolsets", None)
1248
+ _w(f" enabled_toolsets: {enabled!r}")
1249
+ tool_names = getattr(child, "valid_tool_names", None)
1250
+ if tool_names:
1251
+ _w(f" loaded tool count: {len(tool_names)}")
1252
+ try:
1253
+ _w(f" loaded tools: {sorted(tool_names)}")
1254
+ except Exception:
1255
+ pass
1256
+ _w("")
1257
+
1258
+ _w("## Prompt / schema sizes")
1259
+ try:
1260
+ sys_prompt = getattr(child, "ephemeral_system_prompt", None) \
1261
+ or getattr(child, "system_prompt", None) \
1262
+ or ""
1263
+ _w(f" system_prompt_bytes: {len(sys_prompt.encode('utf-8')) if isinstance(sys_prompt, str) else 'n/a'}")
1264
+ _w(f" system_prompt_chars: {len(sys_prompt) if isinstance(sys_prompt, str) else 'n/a'}")
1265
+ except Exception as exc:
1266
+ _w(f" system_prompt: <error: {exc}>")
1267
+ try:
1268
+ tools_schema = getattr(child, "tools", None)
1269
+ if tools_schema is not None:
1270
+ _schema_json = json.dumps(tools_schema, default=str)
1271
+ _w(f" tool_schema_count: {len(tools_schema)}")
1272
+ _w(f" tool_schema_bytes: {len(_schema_json.encode('utf-8'))}")
1273
+ except Exception as exc:
1274
+ _w(f" tool_schema: <error: {exc}>")
1275
+ _w("")
1276
+
1277
+ _w("## Activity summary")
1278
+ try:
1279
+ summary = child.get_activity_summary()
1280
+ for k, v in summary.items():
1281
+ _w(f" {k}: {v!r}")
1282
+ except Exception as exc:
1283
+ _w(f" <get_activity_summary failed: {exc}>")
1284
+ _w("")
1285
+
1286
+ _w("## Worker thread stack at timeout")
1287
+ if worker_thread is not None and worker_thread.is_alive():
1288
+ frames = _sys._current_frames()
1289
+ worker_frame = frames.get(worker_thread.ident)
1290
+ if worker_frame is not None:
1291
+ stack = _traceback.format_stack(worker_frame)
1292
+ for frame_line in stack:
1293
+ for sub in frame_line.rstrip().split("\n"):
1294
+ _w(f" {sub}")
1295
+ else:
1296
+ _w(" <worker frame not available>")
1297
+ elif worker_thread is None:
1298
+ _w(" <no worker thread handle>")
1299
+ else:
1300
+ _w(" <worker thread already exited>")
1301
+ _w("")
1302
+
1303
+ _w("## Notes")
1304
+ _w(" This file is written ONLY when a subagent times out with 0 API calls.")
1305
+ _w(" 0-API-call timeouts mean the child never reached its first LLM request.")
1306
+ _w(" Common causes: oversized prompt rejected by provider, transport hang,")
1307
+ _w(" credential resolution stuck. See issue #14726 for context.")
1308
+
1309
+ dump_path.write_text("\n".join(lines), encoding="utf-8")
1310
+ return str(dump_path)
1311
+ except Exception as exc:
1312
+ logger.warning("Subagent timeout diagnostic dump failed: %s", exc)
1313
+ return None
1314
+
1315
+
1316
+ def _run_single_child(
1317
+ task_index: int,
1318
+ goal: str,
1319
+ child=None,
1320
+ parent_agent=None,
1321
+ **_kwargs,
1322
+ ) -> Dict[str, Any]:
1323
+ """
1324
+ Run a pre-built child agent. Called from within a thread.
1325
+ Returns a structured result dict.
1326
+ """
1327
+ child_start = time.monotonic()
1328
+
1329
+ # Get the progress callback from the child agent
1330
+ child_progress_cb = getattr(child, "tool_progress_callback", None)
1331
+
1332
+ # Restore parent tool names using the value saved before child construction
1333
+ # mutated the global. This is the correct parent toolset, not the child's.
1334
+ import model_tools
1335
+
1336
+ _saved_tool_names = getattr(
1337
+ child, "_delegate_saved_tool_names", list(model_tools._last_resolved_tool_names)
1338
+ )
1339
+
1340
+ child_pool = getattr(child, "_credential_pool", None)
1341
+ leased_cred_id = None
1342
+ if child_pool is not None:
1343
+ leased_cred_id = child_pool.acquire_lease()
1344
+ if leased_cred_id is not None:
1345
+ try:
1346
+ leased_entry = child_pool.current()
1347
+ if leased_entry is not None and hasattr(child, "_swap_credential"):
1348
+ child._swap_credential(leased_entry)
1349
+ except Exception as exc:
1350
+ logger.debug("Failed to bind child to leased credential: %s", exc)
1351
+
1352
+ # Heartbeat: periodically propagate child activity to the parent so the
1353
+ # gateway inactivity timeout doesn't fire while the subagent is working.
1354
+ # Without this, the parent's _last_activity_ts freezes when delegate_task
1355
+ # starts and the gateway eventually kills the agent for "no activity".
1356
+ _heartbeat_stop = threading.Event()
1357
+ # Stale detection: track the child's (tool, iteration) pair across
1358
+ # heartbeat cycles. If neither advances, count the cycle as stale.
1359
+ # Different thresholds for idle vs in-tool (see _HEARTBEAT_STALE_CYCLES_*).
1360
+ _last_seen_iter = [0]
1361
+ _last_seen_tool = [None] # type: list
1362
+ _stale_count = [0]
1363
+
1364
+ def _heartbeat_loop():
1365
+ while not _heartbeat_stop.wait(_HEARTBEAT_INTERVAL):
1366
+ if parent_agent is None:
1367
+ continue
1368
+ touch = getattr(parent_agent, "_touch_activity", None)
1369
+ if not touch:
1370
+ continue
1371
+ # Pull detail from the child's own activity tracker
1372
+ desc = f"delegate_task: subagent {task_index} working"
1373
+ try:
1374
+ child_summary = child.get_activity_summary()
1375
+ child_tool = child_summary.get("current_tool")
1376
+ child_iter = child_summary.get("api_call_count", 0)
1377
+ child_max = child_summary.get("max_iterations", 0)
1378
+
1379
+ # Stale detection: count cycles where neither the iteration
1380
+ # count nor the current_tool advances. A child running a
1381
+ # legitimately long-running tool (terminal command, web
1382
+ # fetch) keeps current_tool set but doesn't advance
1383
+ # api_call_count — we don't want that to look stale at the
1384
+ # idle threshold.
1385
+ iter_advanced = child_iter > _last_seen_iter[0]
1386
+ tool_changed = child_tool != _last_seen_tool[0]
1387
+ if iter_advanced or tool_changed:
1388
+ _last_seen_iter[0] = child_iter
1389
+ _last_seen_tool[0] = child_tool
1390
+ _stale_count[0] = 0
1391
+ else:
1392
+ _stale_count[0] += 1
1393
+
1394
+ # Pick threshold based on whether the child is currently
1395
+ # inside a tool call. In-tool threshold is high enough to
1396
+ # cover legitimately slow tools; idle threshold stays
1397
+ # tight so the gateway timeout can fire on a truly wedged
1398
+ # child.
1399
+ stale_limit = (
1400
+ _HEARTBEAT_STALE_CYCLES_IN_TOOL
1401
+ if child_tool
1402
+ else _HEARTBEAT_STALE_CYCLES_IDLE
1403
+ )
1404
+ if _stale_count[0] >= stale_limit:
1405
+ logger.warning(
1406
+ "Subagent %d appears stale (no progress for %d "
1407
+ "heartbeat cycles, tool=%s) — stopping heartbeat",
1408
+ task_index,
1409
+ _stale_count[0],
1410
+ child_tool or "<none>",
1411
+ )
1412
+ break # stop touching parent, let gateway timeout fire
1413
+
1414
+ if child_tool:
1415
+ desc = (
1416
+ f"delegate_task: subagent running {child_tool} "
1417
+ f"(iteration {child_iter}/{child_max})"
1418
+ )
1419
+ else:
1420
+ child_desc = child_summary.get("last_activity_desc", "")
1421
+ if child_desc:
1422
+ desc = (
1423
+ f"delegate_task: subagent {child_desc} "
1424
+ f"(iteration {child_iter}/{child_max})"
1425
+ )
1426
+ except Exception:
1427
+ pass
1428
+ try:
1429
+ touch(desc)
1430
+ except Exception:
1431
+ pass
1432
+
1433
+ _heartbeat_thread = threading.Thread(target=_heartbeat_loop, daemon=True)
1434
+
1435
+ # Register the live agent in the module-level registry so the TUI can
1436
+ # target it by subagent_id (kill, pause, status queries). Unregistered
1437
+ # in the finally block, even when the child raises. Test doubles that
1438
+ # hand us a MagicMock don't carry stable ids; skip registration then.
1439
+ _raw_sid = getattr(child, "_subagent_id", None)
1440
+ _subagent_id = _raw_sid if isinstance(_raw_sid, str) else None
1441
+ if _subagent_id:
1442
+ _raw_depth = getattr(child, "_delegate_depth", 1)
1443
+ _tui_depth = max(0, _raw_depth - 1) if isinstance(_raw_depth, int) else 0
1444
+ _parent_sid = getattr(child, "_parent_subagent_id", None)
1445
+ _register_subagent(
1446
+ {
1447
+ "subagent_id": _subagent_id,
1448
+ "parent_id": _parent_sid if isinstance(_parent_sid, str) else None,
1449
+ "depth": _tui_depth,
1450
+ "goal": goal,
1451
+ "model": (
1452
+ getattr(child, "model", None)
1453
+ if isinstance(getattr(child, "model", None), str)
1454
+ else None
1455
+ ),
1456
+ "started_at": time.time(),
1457
+ "status": "running",
1458
+ "tool_count": 0,
1459
+ "agent": child,
1460
+ }
1461
+ )
1462
+
1463
+ try:
1464
+ _heartbeat_thread.start()
1465
+ if child_progress_cb:
1466
+ try:
1467
+ child_progress_cb("subagent.start", preview=goal)
1468
+ except Exception as e:
1469
+ logger.debug("Progress callback start failed: %s", e)
1470
+
1471
+ # File-state coordination: reuse the stable subagent_id as the child's
1472
+ # task_id so file_state writes, active-subagents registry, and TUI
1473
+ # events all share one key. Falls back to a fresh uuid only if the
1474
+ # pre-built id is somehow missing.
1475
+ import uuid as _uuid
1476
+
1477
+ child_task_id = _subagent_id or f"subagent-{task_index}-{_uuid.uuid4().hex[:8]}"
1478
+ parent_task_id = getattr(parent_agent, "_current_task_id", None)
1479
+ wall_start = time.time()
1480
+ parent_reads_snapshot = (
1481
+ list(file_state.known_reads(parent_task_id)) if parent_task_id else []
1482
+ )
1483
+
1484
+ # Run child with a hard timeout to prevent indefinite blocking
1485
+ # when the child's API call or tool-level HTTP request hangs.
1486
+ child_timeout = _get_child_timeout()
1487
+ _timeout_executor = ThreadPoolExecutor(
1488
+ max_workers=1,
1489
+ # Install a non-interactive approval callback in the worker thread
1490
+ # so dangerous-command prompts from the subagent don't fall back to
1491
+ # input() and deadlock the parent's prompt_toolkit TUI.
1492
+ # Callback (deny vs approve) is governed by delegation.subagent_auto_approve.
1493
+ initializer=_set_subagent_approval_cb,
1494
+ initargs=(_get_subagent_approval_callback(),),
1495
+ )
1496
+ # Capture the worker thread so the timeout diagnostic can dump its
1497
+ # Python stack (see #14726 — 0-API-call hangs are opaque without it).
1498
+ _worker_thread_holder: Dict[str, Optional[threading.Thread]] = {"t": None}
1499
+
1500
+ def _run_with_thread_capture():
1501
+ _worker_thread_holder["t"] = threading.current_thread()
1502
+ return child.run_conversation(
1503
+ user_message=goal,
1504
+ task_id=child_task_id,
1505
+ )
1506
+
1507
+ _child_future = _timeout_executor.submit(_run_with_thread_capture)
1508
+ try:
1509
+ result = _child_future.result(timeout=child_timeout)
1510
+ except Exception as _timeout_exc:
1511
+ # Signal the child to stop so its thread can exit cleanly.
1512
+ try:
1513
+ if hasattr(child, "interrupt"):
1514
+ child.interrupt()
1515
+ elif hasattr(child, "_interrupt_requested"):
1516
+ child._interrupt_requested = True
1517
+ except Exception:
1518
+ pass
1519
+
1520
+ is_timeout = isinstance(_timeout_exc, (FuturesTimeoutError, TimeoutError))
1521
+ duration = round(time.monotonic() - child_start, 2)
1522
+ logger.warning(
1523
+ "Subagent %d %s after %.1fs",
1524
+ task_index,
1525
+ "timed out" if is_timeout else f"raised {type(_timeout_exc).__name__}",
1526
+ duration,
1527
+ )
1528
+
1529
+ # When a subagent times out BEFORE making any API call, dump a
1530
+ # diagnostic to help users (and us) see what the child was doing.
1531
+ # See #14726 — without this, 0-API-call hangs are black boxes.
1532
+ diagnostic_path: Optional[str] = None
1533
+ child_api_calls = 0
1534
+ try:
1535
+ _summary = child.get_activity_summary()
1536
+ child_api_calls = int(_summary.get("api_call_count", 0) or 0)
1537
+ except Exception:
1538
+ pass
1539
+ if is_timeout and child_api_calls == 0:
1540
+ diagnostic_path = _dump_subagent_timeout_diagnostic(
1541
+ child=child,
1542
+ task_index=task_index,
1543
+ timeout_seconds=float(child_timeout),
1544
+ duration_seconds=float(duration),
1545
+ worker_thread=_worker_thread_holder.get("t"),
1546
+ goal=goal,
1547
+ )
1548
+ if diagnostic_path:
1549
+ logger.warning(
1550
+ "Subagent %d 0-API-call timeout — diagnostic written to %s",
1551
+ task_index,
1552
+ diagnostic_path,
1553
+ )
1554
+
1555
+ if child_progress_cb:
1556
+ try:
1557
+ child_progress_cb(
1558
+ "subagent.complete",
1559
+ preview=(
1560
+ f"Timed out after {duration}s"
1561
+ if is_timeout
1562
+ else str(_timeout_exc)
1563
+ ),
1564
+ status="timeout" if is_timeout else "error",
1565
+ duration_seconds=duration,
1566
+ summary="",
1567
+ )
1568
+ except Exception:
1569
+ pass
1570
+
1571
+ if is_timeout:
1572
+ if child_api_calls == 0:
1573
+ _err = (
1574
+ f"Subagent timed out after {child_timeout}s without "
1575
+ f"making any API call — the child never reached its "
1576
+ f"first LLM request (prompt construction, credential "
1577
+ f"resolution, or transport may be stuck)."
1578
+ )
1579
+ if diagnostic_path:
1580
+ _err += f" Diagnostic: {diagnostic_path}"
1581
+ else:
1582
+ _err = (
1583
+ f"Subagent timed out after {child_timeout}s with "
1584
+ f"{child_api_calls} API call(s) completed — likely "
1585
+ f"stuck on a slow API call or unresponsive network request."
1586
+ )
1587
+ else:
1588
+ _err = str(_timeout_exc)
1589
+
1590
+ return {
1591
+ "task_index": task_index,
1592
+ "status": "timeout" if is_timeout else "error",
1593
+ "summary": None,
1594
+ "error": _err,
1595
+ "exit_reason": "timeout" if is_timeout else "error",
1596
+ "api_calls": child_api_calls,
1597
+ "duration_seconds": duration,
1598
+ "_child_role": getattr(child, "_delegate_role", None),
1599
+ "diagnostic_path": diagnostic_path,
1600
+ }
1601
+ finally:
1602
+ # Shut down executor without waiting — if the child thread
1603
+ # is stuck on blocking I/O, wait=True would hang forever.
1604
+ _timeout_executor.shutdown(wait=False)
1605
+
1606
+ # Flush any remaining batched progress to gateway
1607
+ if child_progress_cb and hasattr(child_progress_cb, "_flush"):
1608
+ try:
1609
+ child_progress_cb._flush()
1610
+ except Exception as e:
1611
+ logger.debug("Progress callback flush failed: %s", e)
1612
+
1613
+ duration = round(time.monotonic() - child_start, 2)
1614
+
1615
+ summary = result.get("final_response") or ""
1616
+ completed = result.get("completed", False)
1617
+ interrupted = result.get("interrupted", False)
1618
+ api_calls = result.get("api_calls", 0)
1619
+
1620
+ if interrupted:
1621
+ status = "interrupted"
1622
+ elif summary:
1623
+ # A summary means the subagent produced usable output.
1624
+ # exit_reason ("completed" vs "max_iterations") already
1625
+ # tells the parent *how* the task ended.
1626
+ status = "completed"
1627
+ else:
1628
+ status = "failed"
1629
+
1630
+ # Build tool trace from conversation messages (already in memory).
1631
+ # Uses tool_call_id to correctly pair parallel tool calls with results.
1632
+ tool_trace: list[Dict[str, Any]] = []
1633
+ trace_by_id: Dict[str, Dict[str, Any]] = {}
1634
+ messages = result.get("messages") or []
1635
+ if isinstance(messages, list):
1636
+ for msg in messages:
1637
+ if not isinstance(msg, dict):
1638
+ continue
1639
+ if msg.get("role") == "assistant":
1640
+ for tc in msg.get("tool_calls") or []:
1641
+ fn = tc.get("function", {})
1642
+ entry_t = {
1643
+ "tool": fn.get("name", "unknown"),
1644
+ "args_bytes": len(fn.get("arguments", "")),
1645
+ }
1646
+ tool_trace.append(entry_t)
1647
+ tc_id = tc.get("id")
1648
+ if tc_id:
1649
+ trace_by_id[tc_id] = entry_t
1650
+ elif msg.get("role") == "tool":
1651
+ content = msg.get("content", "")
1652
+ is_error = bool(content and "error" in content[:80].lower())
1653
+ result_meta = {
1654
+ "result_bytes": len(content),
1655
+ "status": "error" if is_error else "ok",
1656
+ }
1657
+ # Match by tool_call_id for parallel calls
1658
+ tc_id = msg.get("tool_call_id")
1659
+ target = trace_by_id.get(tc_id) if tc_id else None
1660
+ if target is not None:
1661
+ target.update(result_meta)
1662
+ elif tool_trace:
1663
+ # Fallback for messages without tool_call_id
1664
+ tool_trace[-1].update(result_meta)
1665
+
1666
+ # Determine exit reason
1667
+ if interrupted:
1668
+ exit_reason = "interrupted"
1669
+ elif completed:
1670
+ exit_reason = "completed"
1671
+ else:
1672
+ exit_reason = "max_iterations"
1673
+
1674
+ # Extract token counts (safe for mock objects)
1675
+ _input_tokens = getattr(child, "session_prompt_tokens", 0)
1676
+ _output_tokens = getattr(child, "session_completion_tokens", 0)
1677
+ _model = getattr(child, "model", None)
1678
+
1679
+ entry: Dict[str, Any] = {
1680
+ "task_index": task_index,
1681
+ "status": status,
1682
+ "summary": summary,
1683
+ "api_calls": api_calls,
1684
+ "duration_seconds": duration,
1685
+ "model": _model if isinstance(_model, str) else None,
1686
+ "exit_reason": exit_reason,
1687
+ "tokens": {
1688
+ "input": (
1689
+ _input_tokens if isinstance(_input_tokens, (int, float)) else 0
1690
+ ),
1691
+ "output": (
1692
+ _output_tokens if isinstance(_output_tokens, (int, float)) else 0
1693
+ ),
1694
+ },
1695
+ "tool_trace": tool_trace,
1696
+ # Captured before the finally block calls child.close() so the
1697
+ # parent thread can fire subagent_stop with the correct role.
1698
+ # Stripped before the dict is serialised back to the model.
1699
+ "_child_role": getattr(child, "_delegate_role", None),
1700
+ # Captured before child.close() so the parent aggregator can fold
1701
+ # the child's total spend into the parent's session cost. Port of
1702
+ # Kilo-Org/kilocode#9448 — previously the footer only reflected the
1703
+ # parent's direct API calls and under-counted subagent-heavy runs.
1704
+ # Stripped before the dict is serialised back to the model.
1705
+ "_child_cost_usd": (
1706
+ float(getattr(child, "session_estimated_cost_usd", 0.0) or 0.0)
1707
+ if isinstance(
1708
+ getattr(child, "session_estimated_cost_usd", 0.0),
1709
+ (int, float),
1710
+ )
1711
+ else 0.0
1712
+ ),
1713
+ }
1714
+ if status == "failed":
1715
+ entry["error"] = result.get("error", "Subagent did not produce a response.")
1716
+
1717
+ # Cross-agent file-state reminder. If this subagent wrote any
1718
+ # files the parent had already read, surface it so the parent
1719
+ # knows to re-read before editing — the scenario that motivated
1720
+ # the registry. We check writes by ANY non-parent task_id (not
1721
+ # just this child's), which also covers transitive writes from
1722
+ # nested orchestrator→worker chains.
1723
+ try:
1724
+ if parent_task_id and parent_reads_snapshot:
1725
+ sibling_writes = file_state.writes_since(
1726
+ parent_task_id, wall_start, parent_reads_snapshot
1727
+ )
1728
+ if sibling_writes:
1729
+ mod_paths = sorted(
1730
+ {p for paths in sibling_writes.values() for p in paths}
1731
+ )
1732
+ if mod_paths:
1733
+ reminder = (
1734
+ "\n\n[NOTE: subagent modified files the parent "
1735
+ "previously read — re-read before editing: "
1736
+ + ", ".join(mod_paths[:8])
1737
+ + (
1738
+ f" (+{len(mod_paths) - 8} more)"
1739
+ if len(mod_paths) > 8
1740
+ else ""
1741
+ )
1742
+ + "]"
1743
+ )
1744
+ if entry.get("summary"):
1745
+ entry["summary"] = entry["summary"] + reminder
1746
+ else:
1747
+ entry["stale_paths"] = mod_paths
1748
+ except Exception:
1749
+ logger.debug("file_state sibling-write check failed", exc_info=True)
1750
+
1751
+ # Per-branch observability payload: tokens, cost, files touched, and
1752
+ # a tail of tool-call results. Fed into the TUI's overlay detail
1753
+ # pane + accordion rollups (features 1, 2, 4). All fields are
1754
+ # optional — missing data degrades gracefully on the client.
1755
+ _cost_usd = getattr(child, "session_estimated_cost_usd", None)
1756
+ _reasoning_tokens = getattr(child, "session_reasoning_tokens", 0)
1757
+ try:
1758
+ _files_read = list(file_state.known_reads(child_task_id))[:40]
1759
+ except Exception:
1760
+ _files_read = []
1761
+ try:
1762
+ _files_written_map = file_state.writes_since(
1763
+ "", wall_start, []
1764
+ ) # all writes since wall_start
1765
+ except Exception:
1766
+ _files_written_map = {}
1767
+ _files_written = sorted(
1768
+ {
1769
+ p
1770
+ for tid, paths in _files_written_map.items()
1771
+ if tid == child_task_id
1772
+ for p in paths
1773
+ }
1774
+ )[:40]
1775
+
1776
+ _output_tail = _extract_output_tail(result, max_entries=8, max_chars=600)
1777
+
1778
+ complete_kwargs: Dict[str, Any] = {
1779
+ "preview": summary[:160] if summary else entry.get("error", ""),
1780
+ "status": status,
1781
+ "duration_seconds": duration,
1782
+ "summary": summary[:500] if summary else entry.get("error", ""),
1783
+ "input_tokens": (
1784
+ int(_input_tokens) if isinstance(_input_tokens, (int, float)) else 0
1785
+ ),
1786
+ "output_tokens": (
1787
+ int(_output_tokens) if isinstance(_output_tokens, (int, float)) else 0
1788
+ ),
1789
+ "reasoning_tokens": (
1790
+ int(_reasoning_tokens)
1791
+ if isinstance(_reasoning_tokens, (int, float))
1792
+ else 0
1793
+ ),
1794
+ "api_calls": int(api_calls) if isinstance(api_calls, (int, float)) else 0,
1795
+ "files_read": _files_read,
1796
+ "files_written": _files_written,
1797
+ "output_tail": _output_tail,
1798
+ }
1799
+ if _cost_usd is not None:
1800
+ try:
1801
+ complete_kwargs["cost_usd"] = float(_cost_usd)
1802
+ except (TypeError, ValueError):
1803
+ pass
1804
+
1805
+ if child_progress_cb:
1806
+ try:
1807
+ child_progress_cb("subagent.complete", **complete_kwargs)
1808
+ except Exception as e:
1809
+ logger.debug("Progress callback completion failed: %s", e)
1810
+
1811
+ return entry
1812
+
1813
+ except Exception as exc:
1814
+ duration = round(time.monotonic() - child_start, 2)
1815
+ logging.exception(f"[subagent-{task_index}] failed")
1816
+ if child_progress_cb:
1817
+ try:
1818
+ child_progress_cb(
1819
+ "subagent.complete",
1820
+ preview=str(exc),
1821
+ status="failed",
1822
+ duration_seconds=duration,
1823
+ summary=str(exc),
1824
+ )
1825
+ except Exception as e:
1826
+ logger.debug("Progress callback failure relay failed: %s", e)
1827
+ return {
1828
+ "task_index": task_index,
1829
+ "status": "error",
1830
+ "summary": None,
1831
+ "error": str(exc),
1832
+ "api_calls": 0,
1833
+ "duration_seconds": duration,
1834
+ "_child_role": getattr(child, "_delegate_role", None),
1835
+ }
1836
+
1837
+ finally:
1838
+ # Stop the heartbeat thread so it doesn't keep touching parent activity
1839
+ # after the child has finished (or failed). Guard the join: .start()
1840
+ # now lives inside the try block, so if it raised (OS thread
1841
+ # exhaustion) the thread was never started and Thread.join() would
1842
+ # raise RuntimeError. ident is None until start() succeeds.
1843
+ _heartbeat_stop.set()
1844
+ if _heartbeat_thread.ident is not None:
1845
+ _heartbeat_thread.join(timeout=5)
1846
+
1847
+ # Drop the TUI-facing registry entry. Safe to call even if the
1848
+ # child was never registered (e.g. ID missing on test doubles).
1849
+ if _subagent_id:
1850
+ _unregister_subagent(_subagent_id)
1851
+
1852
+ if child_pool is not None and leased_cred_id is not None:
1853
+ try:
1854
+ child_pool.release_lease(leased_cred_id)
1855
+ except Exception as exc:
1856
+ logger.debug("Failed to release credential lease: %s", exc)
1857
+
1858
+ # Restore the parent's tool names so the process-global is correct
1859
+ # for any subsequent execute_code calls or other consumers.
1860
+ import model_tools
1861
+
1862
+ saved_tool_names = getattr(child, "_delegate_saved_tool_names", None)
1863
+ if isinstance(saved_tool_names, list):
1864
+ model_tools._last_resolved_tool_names = list(saved_tool_names)
1865
+
1866
+ # Remove child from active tracking
1867
+
1868
+ # Unregister child from interrupt propagation
1869
+ if hasattr(parent_agent, "_active_children"):
1870
+ try:
1871
+ lock = getattr(parent_agent, "_active_children_lock", None)
1872
+ if lock:
1873
+ with lock:
1874
+ parent_agent._active_children.remove(child)
1875
+ else:
1876
+ parent_agent._active_children.remove(child)
1877
+ except (ValueError, UnboundLocalError) as e:
1878
+ logger.debug("Could not remove child from active_children: %s", e)
1879
+
1880
+ # Close tool resources (terminal sandboxes, browser daemons,
1881
+ # background processes, httpx clients) so subagent subprocesses
1882
+ # don't outlive the delegation.
1883
+ try:
1884
+ if hasattr(child, "close"):
1885
+ child.close()
1886
+ except Exception:
1887
+ logger.debug("Failed to close child agent after delegation")
1888
+
1889
+
1890
+ def _recover_tasks_from_json_string(
1891
+ tasks: Any,
1892
+ ) -> tuple[Optional[List[Dict[str, Any]]], Optional[str]]:
1893
+ if not isinstance(tasks, str):
1894
+ return None, None
1895
+ raw = tasks.strip()
1896
+ if not raw:
1897
+ return None, "Provide either 'goal' (single task) or 'tasks' (batch)."
1898
+ try:
1899
+ parsed = json.loads(raw)
1900
+ except json.JSONDecodeError as exc:
1901
+ return None, (
1902
+ "tasks must be a JSON array of task objects; received a string "
1903
+ f"that could not be parsed as JSON ({exc.msg})."
1904
+ )
1905
+ if not isinstance(parsed, list):
1906
+ return None, (
1907
+ f"tasks must be a JSON array of task objects; parsed "
1908
+ f"{type(parsed).__name__} instead."
1909
+ )
1910
+ return parsed, None
1911
+
1912
+
1913
+ def delegate_task(
1914
+ goal: Optional[str] = None,
1915
+ context: Optional[str] = None,
1916
+ toolsets: Optional[List[str]] = None,
1917
+ tasks: Optional[List[Dict[str, Any]]] = None,
1918
+ max_iterations: Optional[int] = None,
1919
+ acp_command: Optional[str] = None,
1920
+ acp_args: Optional[List[str]] = None,
1921
+ role: Optional[str] = None,
1922
+ parent_agent=None,
1923
+ ) -> str:
1924
+ """
1925
+ Spawn one or more child agents to handle delegated tasks.
1926
+
1927
+ Supports two modes:
1928
+ - Single: provide goal (+ optional context, toolsets, role)
1929
+ - Batch: provide tasks array [{goal, context, toolsets, role}, ...]
1930
+
1931
+ The 'role' parameter controls whether a child can further delegate:
1932
+ 'leaf' (default) cannot; 'orchestrator' retains the delegation
1933
+ toolset and can spawn its own workers, bounded by
1934
+ delegation.max_spawn_depth. Per-task role beats the top-level one.
1935
+
1936
+ Returns JSON with results array, one entry per task.
1937
+ """
1938
+ if parent_agent is None:
1939
+ return tool_error("delegate_task requires a parent agent context.")
1940
+
1941
+ # Operator-controlled kill switch — lets the TUI freeze new fan-out
1942
+ # when a runaway tree is detected, without interrupting already-running
1943
+ # children. Cleared via the matching `delegation.pause` RPC.
1944
+ if is_spawn_paused():
1945
+ return tool_error(
1946
+ "Delegation spawning is paused. Clear the pause via the TUI "
1947
+ "(`p` in /agents) or the `delegation.pause` RPC before retrying."
1948
+ )
1949
+
1950
+ # Normalise the top-level role once; per-task overrides re-normalise.
1951
+ top_role = _normalize_role(role)
1952
+
1953
+ # Depth limit — configurable via delegation.max_spawn_depth,
1954
+ # default 2 for parity with the original MAX_DEPTH constant.
1955
+ depth = getattr(parent_agent, "_delegate_depth", 0)
1956
+ max_spawn = _get_max_spawn_depth()
1957
+ if depth >= max_spawn:
1958
+ return json.dumps(
1959
+ {
1960
+ "error": (
1961
+ f"Delegation depth limit reached (depth={depth}, "
1962
+ f"max_spawn_depth={max_spawn}). Raise "
1963
+ f"delegation.max_spawn_depth in config.yaml if deeper "
1964
+ f"nesting is required (cap: {_MAX_SPAWN_DEPTH_CAP})."
1965
+ )
1966
+ }
1967
+ )
1968
+
1969
+ # Load config
1970
+ cfg = _load_config()
1971
+ default_max_iter = cfg.get("max_iterations", DEFAULT_MAX_ITERATIONS)
1972
+ # Model-supplied max_iterations is ignored — the config value is authoritative
1973
+ # so users get predictable budgets. The kwarg is retained for internal callers
1974
+ # and tests; a model-emitted value here would only shrink the budget and
1975
+ # surprise the user mid-run. Log and drop it if one slips through from a
1976
+ # cached tool schema or a stale provider.
1977
+ if max_iterations is not None and max_iterations != default_max_iter:
1978
+ logger.debug(
1979
+ "delegate_task: ignoring caller-supplied max_iterations=%s; "
1980
+ "using delegation.max_iterations=%s from config",
1981
+ max_iterations, default_max_iter,
1982
+ )
1983
+ effective_max_iter = default_max_iter
1984
+
1985
+ # Resolve delegation credentials (provider:model pair).
1986
+ # When delegation.provider is configured, this resolves the full credential
1987
+ # bundle (base_url, api_key, api_mode) via the same runtime provider system
1988
+ # used by CLI/gateway startup. When unconfigured, returns None values so
1989
+ # children inherit from the parent.
1990
+ try:
1991
+ creds = _resolve_delegation_credentials(cfg, parent_agent)
1992
+ except ValueError as exc:
1993
+ return tool_error(str(exc))
1994
+
1995
+ # Normalize to task list
1996
+ max_children = _get_max_concurrent_children()
1997
+ recovered_tasks, tasks_error = _recover_tasks_from_json_string(tasks)
1998
+ if tasks_error:
1999
+ return tool_error(tasks_error)
2000
+ if recovered_tasks is not None:
2001
+ tasks = recovered_tasks
2002
+
2003
+ if tasks and isinstance(tasks, list):
2004
+ if len(tasks) > max_children:
2005
+ return tool_error(
2006
+ f"Too many tasks: {len(tasks)} provided, but "
2007
+ f"max_concurrent_children is {max_children}. "
2008
+ f"Either reduce the task count, split into multiple "
2009
+ f"delegate_task calls, or increase "
2010
+ f"delegation.max_concurrent_children in config.yaml."
2011
+ )
2012
+ task_list = tasks
2013
+ elif goal and isinstance(goal, str) and goal.strip():
2014
+ task_list = [
2015
+ {"goal": goal, "context": context, "toolsets": toolsets, "role": top_role}
2016
+ ]
2017
+ else:
2018
+ return tool_error("Provide either 'goal' (single task) or 'tasks' (batch).")
2019
+
2020
+ if not task_list:
2021
+ return tool_error("No tasks provided.")
2022
+
2023
+ # Validate each task has a goal
2024
+ for i, task in enumerate(task_list):
2025
+ if not isinstance(task, dict):
2026
+ return tool_error(
2027
+ f"Task {i} must be an object, got {type(task).__name__}."
2028
+ )
2029
+ if not task.get("goal", "").strip():
2030
+ return tool_error(f"Task {i} is missing a 'goal'.")
2031
+
2032
+ overall_start = time.monotonic()
2033
+ results = []
2034
+
2035
+ n_tasks = len(task_list)
2036
+ # Track goal labels for progress display (truncated for readability)
2037
+ task_labels = [t["goal"][:40] for t in task_list]
2038
+
2039
+ # Save parent tool names BEFORE any child construction mutates the global.
2040
+ # _build_child_agent() calls AIAgent() which calls get_tool_definitions(),
2041
+ # which overwrites model_tools._last_resolved_tool_names with child's toolset.
2042
+ import model_tools as _model_tools
2043
+
2044
+ _parent_tool_names = list(_model_tools._last_resolved_tool_names)
2045
+
2046
+ # Build all child agents on the main thread (thread-safe construction)
2047
+ # Wrapped in try/finally so the global is always restored even if a
2048
+ # child build raises (otherwise _last_resolved_tool_names stays corrupted).
2049
+ children = []
2050
+ try:
2051
+ for i, t in enumerate(task_list):
2052
+ task_acp_args = t.get("acp_args") if "acp_args" in t else None
2053
+ # Per-task role beats top-level; normalise again so unknown
2054
+ # per-task values warn and degrade to leaf uniformly.
2055
+ effective_role = _normalize_role(t.get("role") or top_role)
2056
+ child = _build_child_agent(
2057
+ task_index=i,
2058
+ goal=t["goal"],
2059
+ context=t.get("context"),
2060
+ toolsets=t.get("toolsets") or toolsets,
2061
+ model=creds["model"],
2062
+ max_iterations=effective_max_iter,
2063
+ task_count=n_tasks,
2064
+ parent_agent=parent_agent,
2065
+ override_provider=creds["provider"],
2066
+ override_base_url=creds["base_url"],
2067
+ override_api_key=creds["api_key"],
2068
+ override_api_mode=creds["api_mode"],
2069
+ override_acp_command=t.get("acp_command")
2070
+ or acp_command
2071
+ or creds.get("command"),
2072
+ override_acp_args=(
2073
+ task_acp_args
2074
+ if task_acp_args is not None
2075
+ else (acp_args if acp_args is not None else creds.get("args"))
2076
+ ),
2077
+ role=effective_role,
2078
+ )
2079
+ # Override with correct parent tool names (before child construction mutated global)
2080
+ child._delegate_saved_tool_names = _parent_tool_names
2081
+ children.append((i, t, child))
2082
+ finally:
2083
+ # Authoritative restore: reset global to parent's tool names after all children built
2084
+ _model_tools._last_resolved_tool_names = _parent_tool_names
2085
+
2086
+ if n_tasks == 1:
2087
+ # Single task -- run directly (no thread pool overhead)
2088
+ _i, _t, child = children[0]
2089
+ result = _run_single_child(0, _t["goal"], child, parent_agent)
2090
+ results.append(result)
2091
+ else:
2092
+ # Batch -- run in parallel with per-task progress lines
2093
+ completed_count = 0
2094
+ spinner_ref = getattr(parent_agent, "_delegate_spinner", None)
2095
+
2096
+ with ThreadPoolExecutor(max_workers=max_children) as executor:
2097
+ futures = {}
2098
+ for i, t, child in children:
2099
+ future = executor.submit(
2100
+ _run_single_child,
2101
+ task_index=i,
2102
+ goal=t["goal"],
2103
+ child=child,
2104
+ parent_agent=parent_agent,
2105
+ )
2106
+ futures[future] = i
2107
+
2108
+ # Poll futures with interrupt checking. as_completed() blocks
2109
+ # until ALL futures finish — if a child agent gets stuck,
2110
+ # the parent blocks forever even after interrupt propagation.
2111
+ # Instead, use wait() with a short timeout so we can bail
2112
+ # when the parent is interrupted.
2113
+ # Map task_index -> child agent, so fabricated entries for
2114
+ # still-pending futures can carry the correct _delegate_role.
2115
+ _child_by_index = {i: child for (i, _, child) in children}
2116
+
2117
+ pending = set(futures.keys())
2118
+ while pending:
2119
+ if getattr(parent_agent, "_interrupt_requested", False) is True:
2120
+ # Parent interrupted — collect whatever finished and
2121
+ # abandon the rest. Children already received the
2122
+ # interrupt signal; we just can't wait forever.
2123
+ for f in pending:
2124
+ idx = futures[f]
2125
+ if f.done():
2126
+ try:
2127
+ entry = f.result()
2128
+ except Exception as exc:
2129
+ entry = {
2130
+ "task_index": idx,
2131
+ "status": "error",
2132
+ "summary": None,
2133
+ "error": str(exc),
2134
+ "api_calls": 0,
2135
+ "duration_seconds": 0,
2136
+ "_child_role": getattr(
2137
+ _child_by_index.get(idx), "_delegate_role", None
2138
+ ),
2139
+ }
2140
+ else:
2141
+ entry = {
2142
+ "task_index": idx,
2143
+ "status": "interrupted",
2144
+ "summary": None,
2145
+ "error": "Parent agent interrupted — child did not finish in time",
2146
+ "api_calls": 0,
2147
+ "duration_seconds": 0,
2148
+ "_child_role": getattr(
2149
+ _child_by_index.get(idx), "_delegate_role", None
2150
+ ),
2151
+ }
2152
+ results.append(entry)
2153
+ completed_count += 1
2154
+ break
2155
+
2156
+ from concurrent.futures import wait as _cf_wait, FIRST_COMPLETED
2157
+
2158
+ done, pending = _cf_wait(
2159
+ pending, timeout=0.5, return_when=FIRST_COMPLETED
2160
+ )
2161
+ for future in done:
2162
+ try:
2163
+ entry = future.result()
2164
+ except Exception as exc:
2165
+ idx = futures[future]
2166
+ entry = {
2167
+ "task_index": idx,
2168
+ "status": "error",
2169
+ "summary": None,
2170
+ "error": str(exc),
2171
+ "api_calls": 0,
2172
+ "duration_seconds": 0,
2173
+ "_child_role": getattr(
2174
+ _child_by_index.get(idx), "_delegate_role", None
2175
+ ),
2176
+ }
2177
+ results.append(entry)
2178
+ completed_count += 1
2179
+
2180
+ # Print per-task completion line above the spinner
2181
+ idx = entry["task_index"]
2182
+ label = (
2183
+ task_labels[idx] if idx < len(task_labels) else f"Task {idx}"
2184
+ )
2185
+ dur = entry.get("duration_seconds", 0)
2186
+ status = entry.get("status", "?")
2187
+ icon = "✓" if status == "completed" else "✗"
2188
+ remaining = n_tasks - completed_count
2189
+ completion_line = f"{icon} [{idx+1}/{n_tasks}] {label} ({dur}s)"
2190
+ if spinner_ref:
2191
+ try:
2192
+ spinner_ref.print_above(completion_line)
2193
+ except Exception:
2194
+ print(f" {completion_line}")
2195
+ else:
2196
+ print(f" {completion_line}")
2197
+
2198
+ # Update spinner text to show remaining count
2199
+ if spinner_ref and remaining > 0:
2200
+ try:
2201
+ spinner_ref.update_text(
2202
+ f"🔀 {remaining} task{'s' if remaining != 1 else ''} remaining"
2203
+ )
2204
+ except Exception as e:
2205
+ logger.debug("Spinner update_text failed: %s", e)
2206
+
2207
+ # Sort by task_index so results match input order
2208
+ results.sort(key=lambda r: r["task_index"])
2209
+
2210
+ # Notify parent's memory provider of delegation outcomes
2211
+ if (
2212
+ parent_agent
2213
+ and hasattr(parent_agent, "_memory_manager")
2214
+ and parent_agent._memory_manager
2215
+ ):
2216
+ for entry in results:
2217
+ try:
2218
+ _task_goal = (
2219
+ task_list[entry["task_index"]]["goal"]
2220
+ if entry["task_index"] < len(task_list)
2221
+ else ""
2222
+ )
2223
+ parent_agent._memory_manager.on_delegation(
2224
+ task=_task_goal,
2225
+ result=entry.get("summary", "") or "",
2226
+ child_session_id=(
2227
+ getattr(children[entry["task_index"]][2], "session_id", "")
2228
+ if entry["task_index"] < len(children)
2229
+ else ""
2230
+ ),
2231
+ )
2232
+ except Exception:
2233
+ pass
2234
+
2235
+ # Fire subagent_stop hooks once per child, serialised on the parent thread.
2236
+ # This keeps Python-plugin and shell-hook callbacks off of the worker threads
2237
+ # that ran the children, so hook authors don't need to reason about
2238
+ # concurrent invocation. Role was captured into the entry dict in
2239
+ # _run_single_child (or the fabricated-entry branches above) before the
2240
+ # child was closed.
2241
+ _parent_session_id = getattr(parent_agent, "session_id", None)
2242
+ try:
2243
+ from hermes_cli.plugins import invoke_hook as _invoke_hook
2244
+ except Exception:
2245
+ _invoke_hook = None
2246
+ # Aggregate child spend here so the parent's footer/UI reflect the true
2247
+ # cost of a subagent-heavy turn. Port of Kilo-Org/kilocode#9448. Each
2248
+ # child's cost was captured in _run_single_child before its AIAgent was
2249
+ # closed; we fold them into the parent in one pass alongside the
2250
+ # subagent_stop hook loop so we don't walk `results` twice.
2251
+ _children_cost_total = 0.0
2252
+ for entry in results:
2253
+ child_role = entry.pop("_child_role", None)
2254
+ child_cost = entry.pop("_child_cost_usd", 0.0)
2255
+ try:
2256
+ if child_cost:
2257
+ _children_cost_total += float(child_cost)
2258
+ except (TypeError, ValueError):
2259
+ pass
2260
+ if _invoke_hook is None:
2261
+ continue
2262
+ try:
2263
+ _invoke_hook(
2264
+ "subagent_stop",
2265
+ parent_session_id=_parent_session_id,
2266
+ child_role=child_role,
2267
+ child_summary=entry.get("summary"),
2268
+ child_status=entry.get("status"),
2269
+ duration_ms=int((entry.get("duration_seconds") or 0) * 1000),
2270
+ )
2271
+ except Exception:
2272
+ logger.debug("subagent_stop hook invocation failed", exc_info=True)
2273
+
2274
+ # Fold the aggregated child cost into the parent's session total. This is
2275
+ # additive — each delegate_task call contributes its own children — so
2276
+ # nested orchestrator→worker trees roll up naturally: each layer's own
2277
+ # delegate_task() folds its direct children in, and when the orchestrator
2278
+ # itself finishes, its parent folds the orchestrator's now-inflated total
2279
+ # on top. Degrades silently if the parent lacks the counter (older test
2280
+ # fixtures, etc.).
2281
+ if _children_cost_total > 0.0:
2282
+ try:
2283
+ current = float(getattr(parent_agent, "session_estimated_cost_usd", 0.0) or 0.0)
2284
+ parent_agent.session_estimated_cost_usd = current + _children_cost_total
2285
+ # Upgrade the cost_source so the UI doesn't label a partially-real
2286
+ # total as "none" when the parent itself hadn't billed any calls
2287
+ # yet (rare but possible when the parent's only action this turn
2288
+ # was delegate_task).
2289
+ if getattr(parent_agent, "session_cost_source", "none") in {None, "", "none"}:
2290
+ parent_agent.session_cost_source = "subagent"
2291
+ if getattr(parent_agent, "session_cost_status", "unknown") in {None, "", "unknown"}:
2292
+ parent_agent.session_cost_status = "estimated"
2293
+ except Exception:
2294
+ logger.debug("Subagent cost rollup failed", exc_info=True)
2295
+
2296
+ total_duration = round(time.monotonic() - overall_start, 2)
2297
+
2298
+ return json.dumps(
2299
+ {
2300
+ "results": results,
2301
+ "total_duration_seconds": total_duration,
2302
+ },
2303
+ ensure_ascii=False,
2304
+ )
2305
+
2306
+
2307
+ def _resolve_child_credential_pool(effective_provider: Optional[str], parent_agent):
2308
+ """Resolve a credential pool for the child agent.
2309
+
2310
+ Rules:
2311
+ 1. Same provider as the parent -> share the parent's pool so cooldown state
2312
+ and rotation stay synchronized.
2313
+ 2. Different provider -> try to load that provider's own pool.
2314
+ 3. No pool available -> return None and let the child keep the inherited
2315
+ fixed credential behavior.
2316
+ """
2317
+ if not effective_provider:
2318
+ return getattr(parent_agent, "_credential_pool", None)
2319
+
2320
+ parent_provider = getattr(parent_agent, "provider", None) or ""
2321
+ parent_pool = getattr(parent_agent, "_credential_pool", None)
2322
+ if parent_pool is not None and effective_provider == parent_provider:
2323
+ return parent_pool
2324
+
2325
+ try:
2326
+ from agent.credential_pool import load_pool
2327
+
2328
+ pool = load_pool(effective_provider)
2329
+ if pool is not None and pool.has_credentials():
2330
+ return pool
2331
+ except Exception as exc:
2332
+ logger.debug(
2333
+ "Could not load credential pool for child provider '%s': %s",
2334
+ effective_provider,
2335
+ exc,
2336
+ )
2337
+ return None
2338
+
2339
+
2340
+ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:
2341
+ """Resolve credentials for subagent delegation.
2342
+
2343
+ If ``delegation.base_url`` is configured, subagents use that direct
2344
+ OpenAI-compatible endpoint. ``delegation.api_key`` overrides the key; when
2345
+ omitted, ``api_key`` is returned as ``None`` so ``_build_child_agent``
2346
+ inherits the parent agent's key (``effective_api_key = override_api_key or
2347
+ parent_api_key``). This lets providers that store their key outside
2348
+ ``OPENAI_API_KEY`` (e.g. ``MINIMAX_API_KEY``, ``DASHSCOPE_API_KEY``) work
2349
+ without a duplicate config entry.
2350
+
2351
+ Otherwise, if ``delegation.provider`` is configured, the full credential
2352
+ bundle (base_url, api_key, api_mode, provider) is resolved via the runtime
2353
+ provider system — the same path used by CLI/gateway startup. This lets
2354
+ subagents run on a completely different provider:model pair.
2355
+
2356
+ If neither base_url nor provider is configured, returns None values so the
2357
+ child inherits everything from the parent agent.
2358
+
2359
+ Raises ValueError with a user-friendly message on credential failure.
2360
+ """
2361
+ configured_model = str(cfg.get("model") or "").strip() or None
2362
+ configured_provider = str(cfg.get("provider") or "").strip() or None
2363
+ configured_base_url = str(cfg.get("base_url") or "").strip() or None
2364
+ configured_api_key = str(cfg.get("api_key") or "").strip() or None
2365
+ configured_api_mode = str(cfg.get("api_mode") or "").strip().lower() or None
2366
+
2367
+ if configured_base_url:
2368
+ # When delegation.api_key is not set, return None so _build_child_agent
2369
+ # falls back to the parent agent's API key via the credential inheritance
2370
+ # path (effective_api_key = override_api_key or parent_api_key). This
2371
+ # lets providers that store their key in a non-OPENAI_API_KEY env var
2372
+ # (e.g. MINIMAX_API_KEY, DASHSCOPE_API_KEY) work without requiring
2373
+ # callers to duplicate the key under delegation.api_key.
2374
+ api_key = configured_api_key # None → inherited from parent in _build_child_agent
2375
+
2376
+ # Use the shared URL-based api_mode detector (same path the main agent's
2377
+ # runtime resolver uses) so Anthropic-compatible direct endpoints with a
2378
+ # /anthropic suffix — Azure AI Foundry, MiniMax, Zhipu GLM, LiteLLM
2379
+ # proxies — pick the right transport automatically. Without this,
2380
+ # subagents would default to chat_completions and hit 404s on endpoints
2381
+ # that only speak the Anthropic Messages protocol. Fixes #10213.
2382
+ from hermes_cli.runtime_provider import _detect_api_mode_for_url
2383
+
2384
+ base_lower = configured_base_url.lower()
2385
+ provider = "custom"
2386
+ api_mode = _detect_api_mode_for_url(configured_base_url) or "chat_completions"
2387
+ if (
2388
+ base_url_hostname(configured_base_url) == "chatgpt.com"
2389
+ and "/backend-api/codex" in base_lower
2390
+ ):
2391
+ provider = "openai-codex"
2392
+ api_mode = "codex_responses"
2393
+ elif base_url_hostname(configured_base_url) == "api.anthropic.com":
2394
+ provider = "anthropic"
2395
+ api_mode = "anthropic_messages"
2396
+ elif "api.kimi.com/coding" in base_lower:
2397
+ provider = "custom"
2398
+ api_mode = "anthropic_messages"
2399
+
2400
+ # Explicit delegation.api_mode in config always wins. Lets users force
2401
+ # a transport for non-standard endpoints the URL heuristic can't detect.
2402
+ if configured_api_mode in {"chat_completions", "codex_responses", "anthropic_messages"}:
2403
+ api_mode = configured_api_mode
2404
+
2405
+ return {
2406
+ "model": configured_model,
2407
+ "provider": provider,
2408
+ "base_url": configured_base_url,
2409
+ "api_key": api_key,
2410
+ "api_mode": api_mode,
2411
+ }
2412
+
2413
+ if not configured_provider:
2414
+ # No provider override — child inherits everything from parent
2415
+ return {
2416
+ "model": configured_model,
2417
+ "provider": None,
2418
+ "base_url": None,
2419
+ "api_key": None,
2420
+ "api_mode": None,
2421
+ }
2422
+
2423
+ # Provider is configured — resolve full credentials
2424
+ try:
2425
+ from hermes_cli.runtime_provider import resolve_runtime_provider
2426
+
2427
+ runtime = resolve_runtime_provider(requested=configured_provider, target_model=configured_model)
2428
+ except Exception as exc:
2429
+ raise ValueError(
2430
+ f"Cannot resolve delegation provider '{configured_provider}': {exc}. "
2431
+ f"Check that the provider is configured (API key set, valid provider name), "
2432
+ f"or set delegation.base_url/delegation.api_key for a direct endpoint. "
2433
+ f"Available providers: openrouter, nous, zai, kimi-coding, minimax."
2434
+ ) from exc
2435
+
2436
+ api_key = runtime.get("api_key", "")
2437
+ if not api_key:
2438
+ raise ValueError(
2439
+ f"Delegation provider '{configured_provider}' resolved but has no API key. "
2440
+ f"Set the appropriate environment variable or run 'hermes auth'."
2441
+ )
2442
+
2443
+ return {
2444
+ "model": configured_model or runtime.get("model") or None,
2445
+ "provider": runtime.get("provider"),
2446
+ "base_url": runtime.get("base_url"),
2447
+ "api_key": api_key,
2448
+ "api_mode": runtime.get("api_mode"),
2449
+ "command": runtime.get("command"),
2450
+ "args": list(runtime.get("args") or []),
2451
+ }
2452
+
2453
+
2454
+ def _load_config() -> dict:
2455
+ """Load delegation config from CLI_CONFIG or persistent config.
2456
+
2457
+ Checks the runtime config (cli.py CLI_CONFIG) first, then falls back
2458
+ to the persistent config (hermes_cli/config.py load_config()) so that
2459
+ ``delegation.model`` / ``delegation.provider`` are picked up regardless
2460
+ of the entry point (CLI, gateway, cron).
2461
+ """
2462
+ try:
2463
+ from cli import CLI_CONFIG
2464
+
2465
+ cfg = CLI_CONFIG.get("delegation") or {}
2466
+ if cfg:
2467
+ return cfg
2468
+ except Exception:
2469
+ pass
2470
+ try:
2471
+ from hermes_cli.config import load_config
2472
+
2473
+ full = load_config()
2474
+ return full.get("delegation") or {}
2475
+ except Exception:
2476
+ return {}
2477
+
2478
+
2479
+ # ---------------------------------------------------------------------------
2480
+ # OpenAI Function-Calling Schema
2481
+ # ---------------------------------------------------------------------------
2482
+
2483
+
2484
+ def _build_top_level_description() -> str:
2485
+ """Compose the delegate_task tool description with current runtime limits.
2486
+
2487
+ The model needs to know its actual ceilings (not the framework defaults),
2488
+ otherwise it self-caps at "default 3" / "default 2" even when the user has
2489
+ raised delegation.max_concurrent_children / max_spawn_depth. Called both
2490
+ at module import (to seed DELEGATE_TASK_SCHEMA) and on every
2491
+ get_definitions() call via dynamic_schema_overrides.
2492
+ """
2493
+ try:
2494
+ max_children = _get_max_concurrent_children()
2495
+ except Exception:
2496
+ max_children = _DEFAULT_MAX_CONCURRENT_CHILDREN
2497
+ try:
2498
+ max_depth = _get_max_spawn_depth()
2499
+ except Exception:
2500
+ max_depth = MAX_DEPTH
2501
+ try:
2502
+ orchestrator_on = _get_orchestrator_enabled()
2503
+ except Exception:
2504
+ orchestrator_on = True
2505
+
2506
+ if max_depth >= 2 and orchestrator_on:
2507
+ nesting_clause = (
2508
+ f"Nested delegation IS enabled for this user "
2509
+ f"(max_spawn_depth={max_depth}): pass role='orchestrator' on a "
2510
+ f"child to let it spawn its own workers, up to {max_depth - 1} "
2511
+ f"additional level(s) deep."
2512
+ )
2513
+ elif max_depth >= 2 and not orchestrator_on:
2514
+ nesting_clause = (
2515
+ f"Nested delegation is DISABLED on this install "
2516
+ f"(delegation.orchestrator_enabled=false), even though "
2517
+ f"max_spawn_depth={max_depth}. role='orchestrator' is silently "
2518
+ f"forced to 'leaf'."
2519
+ )
2520
+ else:
2521
+ nesting_clause = (
2522
+ f"Nested delegation is OFF for this user "
2523
+ f"(max_spawn_depth={max_depth}): every child is a leaf and "
2524
+ f"cannot delegate further. Raise delegation.max_spawn_depth in "
2525
+ f"config.yaml to enable nesting."
2526
+ )
2527
+
2528
+ return (
2529
+ "Spawn one or more subagents to work on tasks in isolated contexts. "
2530
+ "Each subagent gets its own conversation, terminal session, and toolset. "
2531
+ "Only the final summary is returned -- intermediate tool results "
2532
+ "never enter your context window.\n\n"
2533
+ "TWO MODES (one of 'goal' or 'tasks' is required):\n"
2534
+ "1. Single task: provide 'goal' (+ optional context, toolsets)\n"
2535
+ f"2. Batch (parallel): provide 'tasks' array with up to {max_children} "
2536
+ f"items concurrently for this user (configured via "
2537
+ f"delegation.max_concurrent_children in config.yaml). "
2538
+ f"All run in parallel and results are returned together. {nesting_clause}\n\n"
2539
+ "WHEN TO USE delegate_task:\n"
2540
+ "- Reasoning-heavy subtasks (debugging, code review, research synthesis)\n"
2541
+ "- Tasks that would flood your context with intermediate data\n"
2542
+ "- Parallel independent workstreams (research A and B simultaneously)\n\n"
2543
+ "WHEN NOT TO USE (use these instead):\n"
2544
+ "- Mechanical multi-step work with no reasoning needed -> use execute_code\n"
2545
+ "- Single tool call -> just call the tool directly\n"
2546
+ "- Tasks needing user interaction -> subagents cannot use clarify\n"
2547
+ "- Durable long-running work that must outlive the current turn -> "
2548
+ "use cronjob (action='create') or terminal(background=True, "
2549
+ "notify_on_complete=True) instead. delegate_task runs SYNCHRONOUSLY "
2550
+ "inside the parent turn: if the parent is interrupted (user sends a "
2551
+ "new message, /stop, /new) the child is cancelled with status="
2552
+ "'interrupted' and its work is discarded. Children cannot continue "
2553
+ "in the background.\n\n"
2554
+ "IMPORTANT:\n"
2555
+ "- Subagents have NO memory of your conversation. Pass all relevant "
2556
+ "info (file paths, error messages, constraints) via the 'context' field.\n"
2557
+ "- If the user is writing in a non-English language, or asked for "
2558
+ "output in a specific language / tone / style, say so in 'context' "
2559
+ "(e.g. \"respond in Chinese\", \"return output in Japanese\"). "
2560
+ "Otherwise subagents default to English and their summaries will "
2561
+ "contaminate your final reply with the wrong language.\n"
2562
+ "- Subagent summaries are SELF-REPORTS, not verified facts. A subagent "
2563
+ "that claims \"uploaded successfully\" or \"file written\" may be wrong. "
2564
+ "For operations with external side-effects (HTTP POST/PUT, remote "
2565
+ "writes, file creation at shared paths, publishing), require the "
2566
+ "subagent to return a verifiable handle (URL, ID, absolute path, HTTP "
2567
+ "status) and verify it yourself — fetch the URL, stat the file, read "
2568
+ "back the content — before telling the user the operation succeeded.\n"
2569
+ "- Leaf subagents (role='leaf', the default) CANNOT call: "
2570
+ "delegate_task, clarify, memory, send_message, execute_code.\n"
2571
+ "- Orchestrator subagents (role='orchestrator') retain "
2572
+ "delegate_task so they can spawn their own workers, but still "
2573
+ "cannot use clarify, memory, send_message, or execute_code. "
2574
+ f"Orchestrators are bounded by max_spawn_depth={max_depth} for this "
2575
+ f"user and can be disabled globally via "
2576
+ "delegation.orchestrator_enabled=false.\n"
2577
+ "- Each subagent gets its own terminal session (separate working directory and state).\n"
2578
+ "- Results are always returned as an array, one entry per task."
2579
+ )
2580
+
2581
+
2582
+ def _build_tasks_param_description() -> str:
2583
+ """Compose the 'tasks' parameter description with current concurrency limit."""
2584
+ try:
2585
+ max_children = _get_max_concurrent_children()
2586
+ except Exception:
2587
+ max_children = _DEFAULT_MAX_CONCURRENT_CHILDREN
2588
+ return (
2589
+ f"Batch mode: tasks to run in parallel (up to {max_children} for this "
2590
+ f"user, set via delegation.max_concurrent_children). Each gets "
2591
+ "its own subagent with isolated context and terminal session. "
2592
+ "When provided, top-level goal/context/toolsets are ignored."
2593
+ )
2594
+
2595
+
2596
+ def _build_role_param_description() -> str:
2597
+ """Compose the 'role' parameter description with current spawn-depth limit."""
2598
+ try:
2599
+ max_depth = _get_max_spawn_depth()
2600
+ except Exception:
2601
+ max_depth = MAX_DEPTH
2602
+ try:
2603
+ orchestrator_on = _get_orchestrator_enabled()
2604
+ except Exception:
2605
+ orchestrator_on = True
2606
+
2607
+ if max_depth >= 2 and orchestrator_on:
2608
+ nesting_note = (
2609
+ f"Nesting IS enabled for this user (max_spawn_depth={max_depth}): "
2610
+ f"orchestrator children can themselves delegate up to {max_depth - 1} "
2611
+ "more level(s) deep."
2612
+ )
2613
+ elif max_depth >= 2 and not orchestrator_on:
2614
+ nesting_note = (
2615
+ "Nesting is currently disabled "
2616
+ "(delegation.orchestrator_enabled=false); 'orchestrator' is "
2617
+ "silently forced to 'leaf'."
2618
+ )
2619
+ else:
2620
+ nesting_note = (
2621
+ f"Nesting is OFF for this user (max_spawn_depth={max_depth}); "
2622
+ "'orchestrator' is silently forced to 'leaf'. Raise "
2623
+ "delegation.max_spawn_depth in config.yaml to enable."
2624
+ )
2625
+
2626
+ return (
2627
+ "Role of the child agent. 'leaf' (default) = focused "
2628
+ "worker, cannot delegate further. 'orchestrator' = can "
2629
+ f"use delegate_task to spawn its own workers. {nesting_note}"
2630
+ )
2631
+
2632
+
2633
+ def _build_dynamic_schema_overrides() -> dict:
2634
+ """Return per-call schema overrides reflecting current config.
2635
+
2636
+ Plugged into ToolEntry.dynamic_schema_overrides so every
2637
+ get_definitions() pass rewrites the description fields to the user's
2638
+ actual limits.
2639
+ """
2640
+ overrides_params = {
2641
+ **DELEGATE_TASK_SCHEMA["parameters"],
2642
+ }
2643
+ # Deep-copy properties so we don't mutate the static schema dict.
2644
+ overrides_params["properties"] = {
2645
+ k: dict(v) for k, v in DELEGATE_TASK_SCHEMA["parameters"]["properties"].items()
2646
+ }
2647
+ overrides_params["properties"]["tasks"]["description"] = _build_tasks_param_description()
2648
+ overrides_params["properties"]["role"]["description"] = _build_role_param_description()
2649
+ return {
2650
+ "description": _build_top_level_description(),
2651
+ "parameters": overrides_params,
2652
+ }
2653
+
2654
+
2655
+ DELEGATE_TASK_SCHEMA = {
2656
+ "name": "delegate_task",
2657
+ # NOTE: description / tasks.description / role.description are placeholder
2658
+ # values. The real text is generated per get_definitions() call by
2659
+ # _build_dynamic_schema_overrides() (registered via
2660
+ # dynamic_schema_overrides below) so the model sees the user's actual
2661
+ # delegation.max_concurrent_children / max_spawn_depth, not the framework
2662
+ # defaults. Building these lazily (instead of at module import) also
2663
+ # avoids forcing cli.CLI_CONFIG to load before the test conftest can
2664
+ # redirect HERMES_HOME.
2665
+ "description": (
2666
+ "Spawn one or more subagents in isolated contexts. "
2667
+ "Description is rebuilt at every get_definitions() call to reflect "
2668
+ "the user's current delegation limits."
2669
+ ),
2670
+ "parameters": {
2671
+ "type": "object",
2672
+ "properties": {
2673
+ "goal": {
2674
+ "type": "string",
2675
+ "description": (
2676
+ "What the subagent should accomplish. Be specific and "
2677
+ "self-contained -- the subagent knows nothing about your "
2678
+ "conversation history."
2679
+ ),
2680
+ },
2681
+ "context": {
2682
+ "type": "string",
2683
+ "description": (
2684
+ "Background information the subagent needs: file paths, "
2685
+ "error messages, project structure, constraints. The more "
2686
+ "specific you are, the better the subagent performs."
2687
+ ),
2688
+ },
2689
+ "toolsets": {
2690
+ "type": "array",
2691
+ "items": {"type": "string"},
2692
+ "description": (
2693
+ "Toolsets to enable for this subagent. "
2694
+ "Default: inherits your enabled toolsets. "
2695
+ f"Available toolsets: {_TOOLSET_LIST_STR}. "
2696
+ "Common patterns: ['terminal', 'file'] for code work, "
2697
+ "['web'] for research, ['browser'] for web interaction, "
2698
+ "['terminal', 'file', 'web'] for full-stack tasks."
2699
+ ),
2700
+ },
2701
+ "tasks": {
2702
+ "type": "array",
2703
+ "items": {
2704
+ "type": "object",
2705
+ "properties": {
2706
+ "goal": {"type": "string", "description": "Task goal"},
2707
+ "context": {
2708
+ "type": "string",
2709
+ "description": "Task-specific context",
2710
+ },
2711
+ "toolsets": {
2712
+ "type": "array",
2713
+ "items": {"type": "string"},
2714
+ "description": f"Toolsets for this specific task. Available: {_TOOLSET_LIST_STR}. Use 'web' for network access, 'terminal' for shell, 'browser' for web interaction.",
2715
+ },
2716
+ "acp_command": {
2717
+ "type": "string",
2718
+ "description": (
2719
+ "Per-task ACP command override (e.g. 'copilot'). "
2720
+ "Overrides the top-level acp_command for this task only. "
2721
+ "Do NOT set unless the user explicitly told you an ACP CLI is installed."
2722
+ ),
2723
+ },
2724
+ "acp_args": {
2725
+ "type": "array",
2726
+ "items": {"type": "string"},
2727
+ "description": "Per-task ACP args override. Leave empty unless acp_command is set.",
2728
+ },
2729
+ "role": {
2730
+ "type": "string",
2731
+ "enum": ["leaf", "orchestrator"],
2732
+ "description": "Per-task role override. See top-level 'role' for semantics.",
2733
+ },
2734
+ },
2735
+ "required": ["goal"],
2736
+ },
2737
+ # No maxItems — the runtime limit is configurable via
2738
+ # delegation.max_concurrent_children (default 3) and
2739
+ # enforced with a clear error in delegate_task().
2740
+ "description": "(rebuilt at get_definitions() time)",
2741
+ },
2742
+ "role": {
2743
+ "type": "string",
2744
+ "enum": ["leaf", "orchestrator"],
2745
+ "description": "(rebuilt at get_definitions() time)",
2746
+ },
2747
+ "acp_command": {
2748
+ "type": "string",
2749
+ "description": (
2750
+ "Override ACP command for child agents (e.g. 'copilot'). "
2751
+ "When set, children use ACP subprocess transport instead of inheriting "
2752
+ "the parent's transport. Requires an ACP-compatible CLI "
2753
+ "(currently GitHub Copilot CLI via 'copilot --acp --stdio'). "
2754
+ "See agent/copilot_acp_client.py for the implementation. "
2755
+ "IMPORTANT: Do NOT set this unless the user has explicitly told you "
2756
+ "a specific ACP-compatible CLI is installed and configured. "
2757
+ "Leave empty to use the parent's default transport (Hermes subagents)."
2758
+ ),
2759
+ },
2760
+ "acp_args": {
2761
+ "type": "array",
2762
+ "items": {"type": "string"},
2763
+ "description": (
2764
+ "Arguments for the ACP command (default: ['--acp', '--stdio']). "
2765
+ "Only used when acp_command is set. "
2766
+ "Leave empty unless acp_command is explicitly provided."
2767
+ ),
2768
+ },
2769
+ },
2770
+ "required": [],
2771
+ },
2772
+ }
2773
+
2774
+
2775
+ # --- Registry ---
2776
+ from tools.registry import registry, tool_error
2777
+
2778
+ registry.register(
2779
+ name="delegate_task",
2780
+ toolset="delegation",
2781
+ schema=DELEGATE_TASK_SCHEMA,
2782
+ handler=lambda args, **kw: delegate_task(
2783
+ goal=args.get("goal"),
2784
+ context=args.get("context"),
2785
+ toolsets=args.get("toolsets"),
2786
+ tasks=args.get("tasks"),
2787
+ max_iterations=args.get("max_iterations"),
2788
+ acp_command=args.get("acp_command"),
2789
+ acp_args=args.get("acp_args"),
2790
+ role=args.get("role"),
2791
+ parent_agent=kw.get("parent_agent"),
2792
+ ),
2793
+ check_fn=check_delegate_requirements,
2794
+ emoji="🔀",
2795
+ dynamic_schema_overrides=_build_dynamic_schema_overrides,
2796
+ )
2797
+