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,1832 @@
1
+ """
2
+ Cron job scheduler - executes due jobs.
3
+
4
+ Provides tick() which checks for due jobs and runs them. The gateway
5
+ calls this every 60 seconds from a background thread.
6
+
7
+ Uses a file-based lock (~/.hermes/cron/.tick.lock) so only one tick
8
+ runs at a time if multiple processes overlap.
9
+ """
10
+
11
+ import asyncio
12
+ import concurrent.futures
13
+ import contextvars
14
+ import json
15
+ import logging
16
+ import os
17
+ import shutil
18
+ import subprocess
19
+ import sys
20
+
21
+ # fcntl is Unix-only; on Windows use msvcrt for file locking
22
+ try:
23
+ import fcntl
24
+ except ImportError:
25
+ fcntl = None
26
+ try:
27
+ import msvcrt
28
+ except ImportError:
29
+ msvcrt = None
30
+ from pathlib import Path
31
+ from typing import List, Optional
32
+
33
+ # Add parent directory to path for imports BEFORE repo-level imports.
34
+ # Without this, standalone invocations (e.g. after `hermes update` reloads
35
+ # the module) fail with ModuleNotFoundError for calvyn_time et al.
36
+ sys.path.insert(0, str(Path(__file__).parent.parent))
37
+
38
+ from calvyn_constants import get_hermes_home
39
+ from hermes_cli.config import load_config, _expand_env_vars
40
+ from calvyn_time import now as _hermes_now
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+
45
+ class CronPromptInjectionBlocked(Exception):
46
+ """Raised by _build_job_prompt when the fully-assembled prompt trips the
47
+ injection scanner. Caught in run_job so the operator sees a clean
48
+ "job blocked" delivery instead of the scheduler crashing.
49
+
50
+ Assembled-prompt scanning (including loaded skill content) plugs the
51
+ gap from #3968: create-time scanning only covers the user-supplied
52
+ prompt field; skill content loaded at runtime was never scanned, so a
53
+ malicious skill could carry an injection payload that reached the
54
+ non-interactive (auto-approve) cron agent.
55
+ """
56
+
57
+
58
+ def _resolve_cron_enabled_toolsets(job: dict, cfg: dict) -> list[str] | None:
59
+ """Resolve the toolset list for a cron job.
60
+
61
+ Precedence:
62
+ 1. Per-job ``enabled_toolsets`` (set via ``cronjob`` tool on create/update).
63
+ Keeps the agent's job-scoped toolset override intact — #6130.
64
+ 2. Per-platform ``hermes tools`` config for the ``cron`` platform.
65
+ Mirrors gateway behavior (``_get_platform_tools(cfg, platform_key)``)
66
+ so users can gate cron toolsets globally without recreating every job.
67
+ 3. ``None`` on any lookup failure — AIAgent loads the full default set
68
+ (legacy behavior before this change, preserved as the safety net).
69
+
70
+ _DEFAULT_OFF_TOOLSETS ({moa, homeassistant, rl}) are removed by
71
+ ``_get_platform_tools`` for unconfigured platforms, so fresh installs
72
+ get cron WITHOUT ``moa`` by default (issue reported by Norbert —
73
+ surprise $4.63 run).
74
+ """
75
+ per_job = job.get("enabled_toolsets")
76
+ if per_job:
77
+ return per_job
78
+ try:
79
+ from hermes_cli.tools_config import _get_platform_tools # lazy: avoid heavy import at cron module load
80
+ return sorted(_get_platform_tools(cfg or {}, "cron"))
81
+ except Exception as exc:
82
+ logger.warning(
83
+ "Cron toolset resolution failed, falling back to full default toolset: %s",
84
+ exc,
85
+ )
86
+ return None
87
+
88
+ # Valid delivery platforms — used to validate user-supplied platform names
89
+ # in cron delivery targets, preventing env var enumeration via crafted names.
90
+ _KNOWN_DELIVERY_PLATFORMS = frozenset({
91
+ "telegram", "discord", "slack", "whatsapp", "signal",
92
+ "matrix", "mattermost", "homeassistant", "dingtalk", "feishu",
93
+ "wecom", "wecom_callback", "weixin", "sms", "email", "webhook", "bluebubbles",
94
+ "qqbot", "yuanbao",
95
+ })
96
+
97
+ # Platforms that support a configured cron/notification home target, mapped to
98
+ # the environment variable used by gateway setup/runtime config.
99
+ _HOME_TARGET_ENV_VARS = {
100
+ "matrix": "MATRIX_HOME_ROOM",
101
+ "telegram": "TELEGRAM_HOME_CHANNEL",
102
+ "discord": "DISCORD_HOME_CHANNEL",
103
+ "slack": "SLACK_HOME_CHANNEL",
104
+ "signal": "SIGNAL_HOME_CHANNEL",
105
+ "mattermost": "MATTERMOST_HOME_CHANNEL",
106
+ "sms": "SMS_HOME_CHANNEL",
107
+ "email": "EMAIL_HOME_ADDRESS",
108
+ "dingtalk": "DINGTALK_HOME_CHANNEL",
109
+ "feishu": "FEISHU_HOME_CHANNEL",
110
+ "wecom": "WECOM_HOME_CHANNEL",
111
+ "weixin": "WEIXIN_HOME_CHANNEL",
112
+ "bluebubbles": "BLUEBUBBLES_HOME_CHANNEL",
113
+ "qqbot": "QQBOT_HOME_CHANNEL",
114
+ "whatsapp": "WHATSAPP_HOME_CHANNEL",
115
+ }
116
+
117
+ # Legacy env var names kept for back-compat. Each entry is the current
118
+ # primary env var → the previous name. _get_home_target_chat_id falls
119
+ # back to the legacy name if the primary is unset, so users who set the
120
+ # old name before the rename keep working until they migrate.
121
+ _LEGACY_HOME_TARGET_ENV_VARS = {
122
+ "QQBOT_HOME_CHANNEL": "QQ_HOME_CHANNEL",
123
+ }
124
+
125
+ from cron.jobs import get_due_jobs, mark_job_run, save_job_output, advance_next_run
126
+
127
+ # Sentinel: when a cron agent has nothing new to report, it can start its
128
+ # response with this marker to suppress delivery. Output is still saved
129
+ # locally for audit.
130
+ SILENT_MARKER = "[SILENT]"
131
+
132
+ # Backward-compatible module override used by tests and emergency monkeypatches.
133
+ _hermes_home: Path | None = None
134
+
135
+
136
+ def _get_hermes_home() -> Path:
137
+ """Resolve Hermes home dynamically while preserving test monkeypatch hooks."""
138
+ return _hermes_home or get_hermes_home()
139
+
140
+
141
+ def _get_lock_paths() -> tuple[Path, Path]:
142
+ """Resolve cron lock paths at call time so profile/env changes are honored."""
143
+ hermes_home = _get_hermes_home()
144
+ lock_dir = hermes_home / "cron"
145
+ return lock_dir, lock_dir / ".tick.lock"
146
+
147
+
148
+ def _resolve_origin(job: dict) -> Optional[dict]:
149
+ """Extract origin info from a job, preserving any extra routing metadata.
150
+
151
+ Treats non-dict origins (free-form provenance strings, ints, lists from
152
+ migration scripts or hand-edited jobs.json) as missing instead of
153
+ crashing with ``AttributeError`` on ``origin.get(...)``. Without this
154
+ guard, a job tagged with e.g. ``"combined-digest-replaces-x-and-y"``
155
+ crashed every fire attempt with
156
+ ``'str' object has no attribute 'get'`` — ``mark_job_run`` recorded the
157
+ failure, but the next tick re-loaded the same poisoned origin and
158
+ crashed identically until the field was patched manually (#18722).
159
+ """
160
+ origin = job.get("origin")
161
+ if not isinstance(origin, dict):
162
+ return None
163
+ platform = origin.get("platform")
164
+ chat_id = origin.get("chat_id")
165
+ if platform and chat_id:
166
+ return origin
167
+ return None
168
+
169
+
170
+ def _plugin_cron_env_var(platform_name: str) -> str:
171
+ """Return the cron home-channel env var registered by a plugin platform.
172
+
173
+ Falls through the platform registry so plugins that set
174
+ ``cron_deliver_env_var`` on their ``PlatformEntry`` get cron delivery
175
+ support without editing this module.
176
+ """
177
+ try:
178
+ from hermes_cli.plugins import discover_plugins
179
+ discover_plugins() # idempotent
180
+ from gateway.platform_registry import platform_registry
181
+ entry = platform_registry.get(platform_name.lower())
182
+ if entry and entry.cron_deliver_env_var:
183
+ return entry.cron_deliver_env_var
184
+ except Exception:
185
+ pass
186
+ return ""
187
+
188
+
189
+ def _is_known_delivery_platform(platform_name: str) -> bool:
190
+ """Whether ``platform_name`` is a valid cron delivery target.
191
+
192
+ Hardcoded built-ins in ``_KNOWN_DELIVERY_PLATFORMS`` are checked first;
193
+ plugin platforms registered via ``PlatformEntry`` are accepted if they
194
+ provide a ``cron_deliver_env_var``.
195
+ """
196
+ name = platform_name.lower()
197
+ if name in _KNOWN_DELIVERY_PLATFORMS:
198
+ return True
199
+ return bool(_plugin_cron_env_var(name))
200
+
201
+
202
+ def _resolve_home_env_var(platform_name: str) -> str:
203
+ """Return the env var name for a platform's cron home channel.
204
+
205
+ Built-in platforms are in ``_HOME_TARGET_ENV_VARS``; plugin platforms are
206
+ resolved from the platform registry.
207
+ """
208
+ name = platform_name.lower()
209
+ env_var = _HOME_TARGET_ENV_VARS.get(name)
210
+ if env_var:
211
+ return env_var
212
+ return _plugin_cron_env_var(name)
213
+
214
+
215
+ def _get_home_target_chat_id(platform_name: str) -> str:
216
+ """Return the configured home target chat/room ID for a delivery platform."""
217
+ env_var = _resolve_home_env_var(platform_name)
218
+ if not env_var:
219
+ return ""
220
+ value = os.getenv(env_var, "")
221
+ if not value:
222
+ legacy = _LEGACY_HOME_TARGET_ENV_VARS.get(env_var)
223
+ if legacy:
224
+ value = os.getenv(legacy, "")
225
+ return value
226
+
227
+
228
+ def _get_home_target_thread_id(platform_name: str) -> Optional[str]:
229
+ """Return the optional thread/topic ID for a platform home target."""
230
+ env_var = _resolve_home_env_var(platform_name)
231
+ if not env_var:
232
+ return None
233
+ value = os.getenv(f"{env_var}_THREAD_ID", "").strip()
234
+ if not value:
235
+ legacy = _LEGACY_HOME_TARGET_ENV_VARS.get(env_var)
236
+ if legacy:
237
+ value = os.getenv(f"{legacy}_THREAD_ID", "").strip()
238
+ return value or None
239
+
240
+
241
+ def _iter_home_target_platforms():
242
+ """Iterate built-in + plugin platform names that expose a home channel.
243
+
244
+ Used by the ``deliver=origin`` fallback when the job has no origin.
245
+ """
246
+ for name in _HOME_TARGET_ENV_VARS:
247
+ yield name
248
+ try:
249
+ from hermes_cli.plugins import discover_plugins
250
+ discover_plugins() # idempotent
251
+ from gateway.platform_registry import platform_registry
252
+ for entry in platform_registry.plugin_entries():
253
+ if entry.cron_deliver_env_var and entry.name not in _HOME_TARGET_ENV_VARS:
254
+ yield entry.name
255
+ except Exception:
256
+ pass
257
+
258
+
259
+ def _resolve_single_delivery_target(job: dict, deliver_value: str) -> Optional[dict]:
260
+ """Resolve one concrete auto-delivery target for a cron job."""
261
+
262
+ origin = _resolve_origin(job)
263
+
264
+ if deliver_value == "local":
265
+ return None
266
+
267
+ if deliver_value == "origin":
268
+ if origin:
269
+ return {
270
+ "platform": origin["platform"],
271
+ "chat_id": str(origin["chat_id"]),
272
+ "thread_id": origin.get("thread_id"),
273
+ }
274
+ # Origin missing (e.g. job created via API/script) — try each
275
+ # platform's home channel as a fallback instead of silently dropping.
276
+ for platform_name in _iter_home_target_platforms():
277
+ chat_id = _get_home_target_chat_id(platform_name)
278
+ if chat_id:
279
+ logger.info(
280
+ "Job '%s' has deliver=origin but no origin; falling back to %s home channel",
281
+ job.get("name", job.get("id", "?")),
282
+ platform_name,
283
+ )
284
+ return {
285
+ "platform": platform_name,
286
+ "chat_id": chat_id,
287
+ "thread_id": _get_home_target_thread_id(platform_name),
288
+ }
289
+ return None
290
+
291
+ if ":" in deliver_value:
292
+ platform_name, rest = deliver_value.split(":", 1)
293
+ platform_key = platform_name.lower()
294
+
295
+ from tools.send_message_tool import _parse_target_ref
296
+
297
+ parsed_chat_id, parsed_thread_id, is_explicit = _parse_target_ref(platform_key, rest)
298
+ if is_explicit:
299
+ chat_id, thread_id = parsed_chat_id, parsed_thread_id
300
+ else:
301
+ chat_id, thread_id = rest, None
302
+
303
+ # Resolve human-friendly labels like "Alice (dm)" to real IDs.
304
+ try:
305
+ from gateway.channel_directory import resolve_channel_name
306
+ resolved = resolve_channel_name(platform_key, chat_id)
307
+ if resolved:
308
+ parsed_chat_id, parsed_thread_id, resolved_is_explicit = _parse_target_ref(platform_key, resolved)
309
+ if resolved_is_explicit:
310
+ chat_id = parsed_chat_id
311
+ if parsed_thread_id is not None:
312
+ thread_id = parsed_thread_id
313
+ else:
314
+ chat_id = resolved
315
+ except Exception:
316
+ pass
317
+
318
+ return {
319
+ "platform": platform_name,
320
+ "chat_id": chat_id,
321
+ "thread_id": thread_id,
322
+ }
323
+
324
+ platform_name = deliver_value
325
+ if origin and origin.get("platform") == platform_name:
326
+ return {
327
+ "platform": platform_name,
328
+ "chat_id": str(origin["chat_id"]),
329
+ "thread_id": origin.get("thread_id"),
330
+ }
331
+
332
+ if not _is_known_delivery_platform(platform_name):
333
+ return None
334
+ chat_id = _get_home_target_chat_id(platform_name)
335
+ if not chat_id:
336
+ return None
337
+
338
+ return {
339
+ "platform": platform_name,
340
+ "chat_id": chat_id,
341
+ "thread_id": _get_home_target_thread_id(platform_name),
342
+ }
343
+
344
+
345
+ def _normalize_deliver_value(deliver) -> str:
346
+ """Normalize a stored/submitted ``deliver`` value to its canonical string form.
347
+
348
+ The contract is that ``deliver`` is a string (``"local"``, ``"origin"``,
349
+ ``"telegram"``, ``"telegram:-1001:17"``, or comma-separated combinations).
350
+ Historically some callers — MCP clients passing an array, direct edits of
351
+ ``jobs.json``, or stale code paths — have stored a list/tuple like
352
+ ``["telegram"]``. ``str(["telegram"])`` would serialize to the literal
353
+ string ``"['telegram']"``, which is not a known platform and fails
354
+ resolution silently. Flatten lists/tuples into a comma-separated string
355
+ so both forms work. Returns ``"local"`` for anything falsy.
356
+ """
357
+ if deliver is None or deliver == "":
358
+ return "local"
359
+ if isinstance(deliver, (list, tuple)):
360
+ parts = [str(p).strip() for p in deliver if str(p).strip()]
361
+ return ",".join(parts) if parts else "local"
362
+ return str(deliver)
363
+
364
+
365
+ # Routing intent tokens — resolved at fire time, not create time, so a
366
+ # job created before Telegram was wired up will pick up Telegram once it
367
+ # comes online. ``all`` expands into the set of connected platforms
368
+ # (those with a configured home chat_id) in _expand_routing_tokens.
369
+ _ROUTING_TOKENS = frozenset({"all"})
370
+
371
+
372
+ def _expand_routing_tokens(part: str) -> List[str]:
373
+ """Expand a routing-intent token to concrete platform names.
374
+
375
+ ``all`` expands to every platform in ``_iter_home_target_platforms()``
376
+ that has a configured home chat_id right now. Unknown / non-token
377
+ values pass through unchanged as a single-element list, so the caller
378
+ can treat every token uniformly.
379
+ """
380
+ token = part.lower()
381
+ if token not in _ROUTING_TOKENS:
382
+ return [part]
383
+ expanded: List[str] = []
384
+ for platform_name in _iter_home_target_platforms():
385
+ if _get_home_target_chat_id(platform_name):
386
+ expanded.append(platform_name)
387
+ return expanded
388
+
389
+
390
+ def _resolve_delivery_targets(job: dict) -> List[dict]:
391
+ """Resolve all concrete auto-delivery targets for a cron job.
392
+
393
+ Accepts the legacy comma-separated ``deliver`` string plus the
394
+ ``all`` routing-intent token, which expands to every platform with
395
+ a configured home channel. Tokens may be combined with explicit
396
+ targets: ``origin,all`` and ``all,telegram:-100:17`` both work.
397
+ Duplicate (platform, chat_id, thread_id) tuples are collapsed by the
398
+ existing dedup pass.
399
+ """
400
+ deliver = _normalize_deliver_value(job.get("deliver", "local"))
401
+ if deliver == "local":
402
+ return []
403
+
404
+ raw_parts = [p.strip() for p in deliver.split(",") if p.strip()]
405
+
406
+ # Expand routing intents.
407
+ parts: List[str] = []
408
+ for raw in raw_parts:
409
+ parts.extend(_expand_routing_tokens(raw))
410
+
411
+ seen = set()
412
+ targets = []
413
+ for part in parts:
414
+ target = _resolve_single_delivery_target(job, part)
415
+ if target:
416
+ key = (target["platform"].lower(), str(target["chat_id"]), target.get("thread_id"))
417
+ if key not in seen:
418
+ seen.add(key)
419
+ targets.append(target)
420
+ return targets
421
+
422
+
423
+ def _resolve_delivery_target(job: dict) -> Optional[dict]:
424
+ """Resolve the concrete auto-delivery target for a cron job, if any."""
425
+ targets = _resolve_delivery_targets(job)
426
+ return targets[0] if targets else None
427
+
428
+
429
+ # Media extension sets — audio routing is centralized in gateway.platforms.base
430
+ # via should_send_media_as_audio() so Telegram-specific rules stay in one place.
431
+ _VIDEO_EXTS = frozenset({'.mp4', '.mov', '.avi', '.mkv', '.webm', '.3gp'})
432
+ _IMAGE_EXTS = frozenset({'.jpg', '.jpeg', '.png', '.webp', '.gif'})
433
+
434
+
435
+ def _send_media_via_adapter(
436
+ adapter,
437
+ chat_id: str,
438
+ media_files: list,
439
+ metadata: dict | None,
440
+ loop,
441
+ job: dict,
442
+ platform=None,
443
+ ) -> None:
444
+ """Send extracted MEDIA files as native platform attachments via a live adapter.
445
+
446
+ Routes each file to the appropriate adapter method (send_voice, send_image_file,
447
+ send_video, send_document) based on file extension — mirroring the routing logic
448
+ in ``BasePlatformAdapter._process_message_background``.
449
+ """
450
+ from pathlib import Path
451
+
452
+ from gateway.platforms.base import should_send_media_as_audio
453
+
454
+ for media_path, _is_voice in media_files:
455
+ try:
456
+ ext = Path(media_path).suffix.lower()
457
+ route_platform = platform if platform is not None else getattr(adapter, "platform", None)
458
+ if should_send_media_as_audio(route_platform, ext, is_voice=_is_voice):
459
+ coro = adapter.send_voice(chat_id=chat_id, audio_path=media_path, metadata=metadata)
460
+ elif ext in _VIDEO_EXTS:
461
+ coro = adapter.send_video(chat_id=chat_id, video_path=media_path, metadata=metadata)
462
+ elif ext in _IMAGE_EXTS:
463
+ coro = adapter.send_image_file(chat_id=chat_id, image_path=media_path, metadata=metadata)
464
+ else:
465
+ coro = adapter.send_document(chat_id=chat_id, file_path=media_path, metadata=metadata)
466
+
467
+ from agent.async_utils import safe_schedule_threadsafe
468
+ future = safe_schedule_threadsafe(coro, loop)
469
+ if future is None:
470
+ logger.warning(
471
+ "Job '%s': cannot send media %s, gateway loop unavailable",
472
+ job.get("id", "?"), media_path,
473
+ )
474
+ return
475
+ try:
476
+ result = future.result(timeout=30)
477
+ except TimeoutError:
478
+ future.cancel()
479
+ raise
480
+ if result and not getattr(result, "success", True):
481
+ logger.warning(
482
+ "Job '%s': media send failed for %s: %s",
483
+ job.get("id", "?"), media_path, getattr(result, "error", "unknown"),
484
+ )
485
+ except Exception as e:
486
+ logger.warning("Job '%s': failed to send media %s: %s", job.get("id", "?"), media_path, e)
487
+
488
+
489
+ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> Optional[str]:
490
+ """
491
+ Deliver job output to the configured target(s) (origin chat, specific platform, etc.).
492
+
493
+ When ``adapters`` and ``loop`` are provided (gateway is running), tries to
494
+ use the live adapter first — this supports E2EE rooms (e.g. Matrix) where
495
+ the standalone HTTP path cannot encrypt. Falls back to standalone send if
496
+ the adapter path fails or is unavailable.
497
+
498
+ Returns None on success, or an error string on failure.
499
+ """
500
+ targets = _resolve_delivery_targets(job)
501
+ if not targets:
502
+ if job.get("deliver", "local") != "local":
503
+ msg = f"no delivery target resolved for deliver={job.get('deliver', 'local')}"
504
+ logger.warning("Job '%s': %s", job["id"], msg)
505
+ return msg
506
+ return None # local-only jobs don't deliver — not a failure
507
+
508
+ from tools.send_message_tool import _send_to_platform
509
+ from gateway.config import load_gateway_config, Platform
510
+
511
+ # Optionally wrap the content with a header/footer so the user knows this
512
+ # is a cron delivery. Wrapping is on by default; set cron.wrap_response: false
513
+ # in config.yaml for clean output.
514
+ wrap_response = True
515
+ try:
516
+ user_cfg = load_config()
517
+ wrap_response = user_cfg.get("cron", {}).get("wrap_response", True)
518
+ except Exception:
519
+ pass
520
+
521
+ if wrap_response:
522
+ task_name = job.get("name", job["id"])
523
+ job_id = job.get("id", "")
524
+ delivery_content = (
525
+ f"Cronjob Response: {task_name}\n"
526
+ f"(job_id: {job_id})\n"
527
+ f"-------------\n\n"
528
+ f"{content}\n\n"
529
+ f"To stop or manage this job, send me a new message (e.g. \"stop reminder {task_name}\")."
530
+ )
531
+ else:
532
+ delivery_content = content
533
+
534
+ # Extract MEDIA: tags so attachments are forwarded as files, not raw text
535
+ from gateway.platforms.base import BasePlatformAdapter
536
+ media_files, cleaned_delivery_content = BasePlatformAdapter.extract_media(delivery_content)
537
+
538
+ try:
539
+ config = load_gateway_config()
540
+ except Exception as e:
541
+ msg = f"failed to load gateway config: {e}"
542
+ logger.error("Job '%s': %s", job["id"], msg)
543
+ return msg
544
+
545
+ delivery_errors = []
546
+
547
+ for target in targets:
548
+ platform_name = target["platform"]
549
+ chat_id = target["chat_id"]
550
+ thread_id = target.get("thread_id")
551
+
552
+ # Diagnostic: log thread_id for topic-aware delivery debugging
553
+ origin = _resolve_origin(job) or {}
554
+ origin_thread = origin.get("thread_id")
555
+ if origin_thread and not thread_id:
556
+ logger.warning(
557
+ "Job '%s': origin has thread_id=%s but delivery target lost it "
558
+ "(deliver=%s, target=%s)",
559
+ job["id"], origin_thread, job.get("deliver", "local"), target,
560
+ )
561
+ elif thread_id:
562
+ logger.debug(
563
+ "Job '%s': delivering to %s:%s thread_id=%s",
564
+ job["id"], platform_name, chat_id, thread_id,
565
+ )
566
+
567
+ # Built-in names resolve to their enum member; plugin platform names
568
+ # create dynamic members via Platform._missing_().
569
+ try:
570
+ platform = Platform(platform_name.lower())
571
+ except (ValueError, KeyError):
572
+ msg = f"unknown platform '{platform_name}'"
573
+ logger.warning("Job '%s': %s", job["id"], msg)
574
+ delivery_errors.append(msg)
575
+ continue
576
+
577
+ pconfig = config.platforms.get(platform)
578
+ if not pconfig or not pconfig.enabled:
579
+ msg = f"platform '{platform_name}' not configured/enabled"
580
+ logger.warning("Job '%s': %s", job["id"], msg)
581
+ delivery_errors.append(msg)
582
+ continue
583
+
584
+ # Prefer the live adapter when the gateway is running — this supports E2EE
585
+ # rooms (e.g. Matrix) where the standalone HTTP path cannot encrypt.
586
+ runtime_adapter = (adapters or {}).get(platform)
587
+ delivered = False
588
+ if runtime_adapter is not None and loop is not None and getattr(loop, "is_running", lambda: False)():
589
+ send_metadata = {"thread_id": thread_id} if thread_id else None
590
+ try:
591
+ # Send cleaned text (MEDIA tags stripped) — not the raw content
592
+ text_to_send = cleaned_delivery_content.strip()
593
+ adapter_ok = True
594
+ if text_to_send:
595
+ from agent.async_utils import safe_schedule_threadsafe
596
+ future = safe_schedule_threadsafe(
597
+ runtime_adapter.send(chat_id, text_to_send, metadata=send_metadata),
598
+ loop,
599
+ )
600
+ if future is None:
601
+ adapter_ok = False
602
+ else:
603
+ try:
604
+ send_result = future.result(timeout=60)
605
+ except TimeoutError:
606
+ future.cancel()
607
+ raise
608
+ if send_result and not getattr(send_result, "success", True):
609
+ err = getattr(send_result, "error", "unknown")
610
+ logger.warning(
611
+ "Job '%s': live adapter send to %s:%s failed (%s), falling back to standalone",
612
+ job["id"], platform_name, chat_id, err,
613
+ )
614
+ adapter_ok = False # fall through to standalone path
615
+
616
+ # Send extracted media files as native attachments via the live adapter
617
+ if adapter_ok and media_files:
618
+ _send_media_via_adapter(
619
+ runtime_adapter,
620
+ chat_id,
621
+ media_files,
622
+ send_metadata,
623
+ loop,
624
+ job,
625
+ platform=platform,
626
+ )
627
+
628
+ if adapter_ok:
629
+ logger.info("Job '%s': delivered to %s:%s via live adapter", job["id"], platform_name, chat_id)
630
+ delivered = True
631
+ except Exception as e:
632
+ logger.warning(
633
+ "Job '%s': live adapter delivery to %s:%s failed (%s), falling back to standalone",
634
+ job["id"], platform_name, chat_id, e,
635
+ )
636
+
637
+ if not delivered:
638
+ # Standalone path: run the async send in a fresh event loop (safe from any thread)
639
+ coro = _send_to_platform(platform, pconfig, chat_id, cleaned_delivery_content, thread_id=thread_id, media_files=media_files)
640
+ try:
641
+ result = asyncio.run(coro)
642
+ except RuntimeError:
643
+ # asyncio.run() checks for a running loop before awaiting the coroutine;
644
+ # when it raises, the original coro was never started — close it to
645
+ # prevent "coroutine was never awaited" RuntimeWarning, then retry in a
646
+ # fresh thread that has no running loop.
647
+ coro.close()
648
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
649
+ future = pool.submit(asyncio.run, _send_to_platform(platform, pconfig, chat_id, cleaned_delivery_content, thread_id=thread_id, media_files=media_files))
650
+ result = future.result(timeout=30)
651
+ except Exception as e:
652
+ msg = f"delivery to {platform_name}:{chat_id} failed: {e}"
653
+ logger.error("Job '%s': %s", job["id"], msg)
654
+ delivery_errors.append(msg)
655
+ continue
656
+
657
+ if result and result.get("error"):
658
+ msg = f"delivery error: {result['error']}"
659
+ logger.error("Job '%s': %s", job["id"], msg)
660
+ delivery_errors.append(msg)
661
+ continue
662
+
663
+ logger.info("Job '%s': delivered to %s:%s", job["id"], platform_name, chat_id)
664
+
665
+ if delivery_errors:
666
+ return "; ".join(delivery_errors)
667
+ return None
668
+
669
+
670
+ _DEFAULT_SCRIPT_TIMEOUT = 120 # seconds
671
+ # Backward-compatible module override used by tests and emergency monkeypatches.
672
+ _SCRIPT_TIMEOUT = _DEFAULT_SCRIPT_TIMEOUT
673
+
674
+
675
+ def _get_script_timeout() -> int:
676
+ """Resolve cron pre-run script timeout from module/env/config with a safe default."""
677
+ if _SCRIPT_TIMEOUT != _DEFAULT_SCRIPT_TIMEOUT:
678
+ try:
679
+ timeout = int(float(_SCRIPT_TIMEOUT))
680
+ if timeout > 0:
681
+ return timeout
682
+ except Exception:
683
+ logger.warning("Invalid patched _SCRIPT_TIMEOUT=%r; using env/config/default", _SCRIPT_TIMEOUT)
684
+
685
+ env_value = os.getenv("HERMES_CRON_SCRIPT_TIMEOUT", "").strip()
686
+ if env_value:
687
+ try:
688
+ timeout = int(float(env_value))
689
+ if timeout > 0:
690
+ return timeout
691
+ except Exception:
692
+ logger.warning("Invalid HERMES_CRON_SCRIPT_TIMEOUT=%r; using config/default", env_value)
693
+
694
+ try:
695
+ cfg = load_config() or {}
696
+ cron_cfg = cfg.get("cron", {}) if isinstance(cfg, dict) else {}
697
+ configured = cron_cfg.get("script_timeout_seconds")
698
+ if configured is not None:
699
+ timeout = int(float(configured))
700
+ if timeout > 0:
701
+ return timeout
702
+ except Exception as exc:
703
+ logger.debug("Failed to load cron script timeout from config: %s", exc)
704
+
705
+ return _DEFAULT_SCRIPT_TIMEOUT
706
+
707
+
708
+ def _run_job_script(script_path: str) -> tuple[bool, str]:
709
+ """Execute a cron job's data-collection script and capture its output.
710
+
711
+ Scripts must reside within HERMES_HOME/scripts/. Both relative and
712
+ absolute paths are resolved and validated against this directory to
713
+ prevent arbitrary script execution via path traversal or absolute
714
+ path injection.
715
+
716
+ Supported interpreters (chosen by file extension):
717
+
718
+ * ``.sh`` / ``.bash`` — run with ``/bin/bash``
719
+ * anything else — run with the current Python interpreter
720
+ (``sys.executable``), preserving the original behaviour for
721
+ Python-based pre-check and data-collection scripts.
722
+
723
+ Shell support lets ``no_agent=True`` jobs ship classic bash watchdogs
724
+ (the `memory-watchdog.sh` pattern) without wrapping them in Python.
725
+
726
+ Args:
727
+ script_path: Path to the script. Relative paths are resolved
728
+ against HERMES_HOME/scripts/. Absolute and ~-prefixed paths
729
+ are also validated to ensure they stay within the scripts dir.
730
+
731
+ Returns:
732
+ (success, output) — on failure *output* contains the error message so the
733
+ LLM can report the problem to the user.
734
+ """
735
+ from calvyn_constants import get_hermes_home
736
+
737
+ scripts_dir = _get_hermes_home() / "scripts"
738
+ scripts_dir.mkdir(parents=True, exist_ok=True)
739
+ scripts_dir_resolved = scripts_dir.resolve()
740
+
741
+ raw = Path(script_path).expanduser()
742
+ if raw.is_absolute():
743
+ path = raw.resolve()
744
+ else:
745
+ path = (scripts_dir / raw).resolve()
746
+
747
+ # Guard against path traversal, absolute path injection, and symlink
748
+ # escape — scripts MUST reside within HERMES_HOME/scripts/.
749
+ try:
750
+ path.relative_to(scripts_dir_resolved)
751
+ except ValueError:
752
+ return False, (
753
+ f"Blocked: script path resolves outside the scripts directory "
754
+ f"({scripts_dir_resolved}): {script_path!r}"
755
+ )
756
+
757
+ if not path.exists():
758
+ return False, f"Script not found: {path}"
759
+ if not path.is_file():
760
+ return False, f"Script path is not a file: {path}"
761
+
762
+ script_timeout = _get_script_timeout()
763
+
764
+ # Pick an interpreter by extension. Bash for .sh/.bash, Python for
765
+ # everything else. We deliberately do NOT honour the file's own
766
+ # shebang: the scripts dir is trusted, but keeping the interpreter
767
+ # choice explicit here keeps the allowed surface small and auditable.
768
+ suffix = path.suffix.lower()
769
+ if suffix in {".sh", ".bash"}:
770
+ # Resolve bash dynamically so Windows (Git Bash) and Linux/macOS
771
+ # all work. On native Windows without Git for Windows installed
772
+ # shutil.which returns None — fall back to a clear error rather
773
+ # than a FileNotFoundError with a confusing "[WinError 2]"
774
+ # traceback.
775
+ _bash = shutil.which("bash") or (
776
+ "/bin/bash" if os.path.isfile("/bin/bash") else None
777
+ )
778
+ if _bash is None:
779
+ return False, (
780
+ f"Cannot run .sh/.bash script {path.name!r}: bash not found on PATH. "
781
+ "On Windows, install Git for Windows (which ships Git Bash) "
782
+ "or rewrite the script as Python (.py)."
783
+ )
784
+ argv = [_bash, str(path)]
785
+ else:
786
+ argv = [sys.executable, str(path)]
787
+
788
+ try:
789
+ result = subprocess.run(
790
+ argv,
791
+ capture_output=True,
792
+ text=True,
793
+ timeout=script_timeout,
794
+ cwd=str(path.parent),
795
+ )
796
+ stdout = (result.stdout or "").strip()
797
+ stderr = (result.stderr or "").strip()
798
+
799
+ # Redact secrets from both stdout and stderr before any return path.
800
+ try:
801
+ from agent.redact import redact_sensitive_text
802
+ stdout = redact_sensitive_text(stdout)
803
+ stderr = redact_sensitive_text(stderr)
804
+ except Exception:
805
+ pass
806
+
807
+ if result.returncode != 0:
808
+ parts = [f"Script exited with code {result.returncode}"]
809
+ if stderr:
810
+ parts.append(f"stderr:\n{stderr}")
811
+ if stdout:
812
+ parts.append(f"stdout:\n{stdout}")
813
+ return False, "\n".join(parts)
814
+
815
+ return True, stdout
816
+
817
+ except subprocess.TimeoutExpired:
818
+ return False, f"Script timed out after {script_timeout}s: {path}"
819
+ except Exception as exc:
820
+ return False, f"Script execution failed: {exc}"
821
+
822
+
823
+ def _parse_wake_gate(script_output: str) -> bool:
824
+ """Parse the last non-empty stdout line of a cron job's pre-check script
825
+ as a wake gate.
826
+
827
+ The convention (ported from nanoclaw #1232): if the last stdout line is
828
+ JSON like ``{"wakeAgent": false}``, the agent is skipped entirely — no
829
+ LLM run, no delivery. Any other output (non-JSON, missing flag, gate
830
+ absent, or ``wakeAgent: true``) means wake the agent normally.
831
+
832
+ Returns True if the agent should wake, False to skip.
833
+ """
834
+ if not script_output:
835
+ return True
836
+ stripped_lines = [line for line in script_output.splitlines() if line.strip()]
837
+ if not stripped_lines:
838
+ return True
839
+ last_line = stripped_lines[-1].strip()
840
+ try:
841
+ gate = json.loads(last_line)
842
+ except (json.JSONDecodeError, ValueError):
843
+ return True
844
+ if not isinstance(gate, dict):
845
+ return True
846
+ return gate.get("wakeAgent", True) is not False
847
+
848
+
849
+ def _build_job_prompt(job: dict, prerun_script: Optional[tuple] = None) -> str:
850
+ """Build the effective prompt for a cron job, optionally loading one or more skills first.
851
+
852
+ Args:
853
+ job: The cron job dict.
854
+ prerun_script: Optional ``(success, stdout)`` from a script that has
855
+ already been executed by the caller (e.g. for a wake-gate check).
856
+ When provided, the script is not re-executed and the cached
857
+ result is used for prompt injection. When omitted, the script
858
+ (if any) runs inline as before.
859
+ """
860
+ prompt = str(job.get("prompt") or "")
861
+ skills = job.get("skills")
862
+
863
+ # Run data-collection script if configured, inject output as context.
864
+ script_path = job.get("script")
865
+ if script_path:
866
+ if prerun_script is not None:
867
+ success, script_output = prerun_script
868
+ else:
869
+ success, script_output = _run_job_script(script_path)
870
+ if success:
871
+ if script_output:
872
+ prompt = (
873
+ "## Script Output\n"
874
+ "The following data was collected by a pre-run script. "
875
+ "Use it as context for your analysis.\n\n"
876
+ f"```\n{script_output}\n```\n\n"
877
+ f"{prompt}"
878
+ )
879
+ else:
880
+ # Script produced no output — nothing to report, skip AI call.
881
+ return None
882
+ else:
883
+ prompt = (
884
+ "## Script Error\n"
885
+ "The data-collection script failed. Report this to the user.\n\n"
886
+ f"```\n{script_output}\n```\n\n"
887
+ f"{prompt}"
888
+ )
889
+
890
+ # Inject output from referenced cron jobs as context.
891
+ context_from = job.get("context_from")
892
+ if context_from:
893
+ from cron.jobs import OUTPUT_DIR
894
+ if isinstance(context_from, str):
895
+ context_from = [context_from]
896
+ for source_job_id in context_from:
897
+ # Guard against path traversal — valid job IDs are 12-char hex strings
898
+ if not source_job_id or not all(c in "0123456789abcdef" for c in source_job_id):
899
+ logger.warning("context_from: skipping invalid job_id %r", source_job_id)
900
+ continue
901
+ try:
902
+ job_output_dir = OUTPUT_DIR / source_job_id
903
+ if not job_output_dir.exists():
904
+ continue # silent skip — no output yet
905
+ output_files = sorted(
906
+ job_output_dir.glob("*.md"),
907
+ key=lambda f: f.stat().st_mtime,
908
+ reverse=True,
909
+ )
910
+ if not output_files:
911
+ continue # silent skip — no output yet
912
+ latest_output = output_files[0].read_text(encoding="utf-8").strip()
913
+ # Truncate to 8K characters to avoid prompt bloat
914
+ _MAX_CONTEXT_CHARS = 8000
915
+ if len(latest_output) > _MAX_CONTEXT_CHARS:
916
+ latest_output = latest_output[:_MAX_CONTEXT_CHARS] + "\n\n[... output truncated ...]"
917
+ if latest_output:
918
+ prompt = (
919
+ f"## Output from job '{source_job_id}'\n"
920
+ "The following is the most recent output from a preceding "
921
+ "cron job. Use it as context for your analysis.\n\n"
922
+ f"```\n{latest_output}\n```\n\n"
923
+ f"{prompt}"
924
+ )
925
+ else:
926
+ continue # silent skip — empty output
927
+ except (OSError, PermissionError) as e:
928
+ logger.warning("context_from: failed to read output for job %r: %s", source_job_id, e)
929
+ # silent skip — do not pollute the prompt with error messages
930
+
931
+ # Always prepend cron execution guidance so the agent knows how
932
+ # delivery works and can suppress delivery when appropriate.
933
+ cron_hint = (
934
+ "[IMPORTANT: You are running as a scheduled cron job. "
935
+ "DELIVERY: Your final response will be automatically delivered "
936
+ "to the user — do NOT use send_message or try to deliver "
937
+ "the output yourself. Just produce your report/output as your "
938
+ "final response and the system handles the rest. "
939
+ "SILENT: If there is genuinely nothing new to report, respond "
940
+ "with exactly \"[SILENT]\" (nothing else) to suppress delivery. "
941
+ "Never combine [SILENT] with content — either report your "
942
+ "findings normally, or say [SILENT] and nothing more.]\n\n"
943
+ )
944
+ prompt = cron_hint + prompt
945
+ if skills is None:
946
+ legacy = job.get("skill")
947
+ skills = [legacy] if legacy else []
948
+ elif isinstance(skills, str):
949
+ skills = [skills]
950
+
951
+ skill_names = [str(name).strip() for name in skills if str(name).strip()]
952
+ if not skill_names:
953
+ return _scan_assembled_cron_prompt(prompt, job)
954
+
955
+ from tools.skills_tool import skill_view
956
+ from tools.skill_usage import bump_use
957
+
958
+ parts = []
959
+ skipped: list[str] = []
960
+ for skill_name in skill_names:
961
+ loaded = json.loads(skill_view(skill_name))
962
+ if not loaded.get("success"):
963
+ error = loaded.get("error") or f"Failed to load skill '{skill_name}'"
964
+ logger.warning("Cron job '%s': skill not found, skipping — %s", job.get("name", job.get("id")), error)
965
+ skipped.append(skill_name)
966
+ continue
967
+
968
+ # Bump usage so the curator sees this skill as actively used.
969
+ try:
970
+ bump_use(skill_name)
971
+ except Exception:
972
+ logger.debug("Cron job: failed to bump skill usage for '%s'", skill_name, exc_info=True)
973
+
974
+ content = str(loaded.get("content") or "").strip()
975
+ if parts:
976
+ parts.append("")
977
+ parts.extend(
978
+ [
979
+ f'[IMPORTANT: The user has invoked the "{skill_name}" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]',
980
+ "",
981
+ content,
982
+ ]
983
+ )
984
+
985
+ if skipped:
986
+ notice = (
987
+ f"[IMPORTANT: The following skill(s) were listed for this job but could not be found "
988
+ f"and were skipped: {', '.join(skipped)}. "
989
+ f"Start your response with a brief notice so the user is aware, e.g.: "
990
+ f"'⚠️ Skill(s) not found and skipped: {', '.join(skipped)}']"
991
+ )
992
+ parts.insert(0, notice)
993
+
994
+ if prompt:
995
+ parts.extend(["", f"The user has provided the following instruction alongside the skill invocation: {prompt}"])
996
+ return _scan_assembled_cron_prompt("\n".join(parts), job)
997
+
998
+
999
+ def _scan_assembled_cron_prompt(assembled: str, job: dict) -> str:
1000
+ """Scan the fully-assembled cron prompt (including skill content) for
1001
+ injection patterns. Raises ``CronPromptInjectionBlocked`` when a match
1002
+ fires so ``run_job`` can surface a clear refusal to the operator.
1003
+
1004
+ Plugs the #3968 gap: ``_scan_cron_prompt`` runs on the user-supplied
1005
+ prompt at create/update, but skill content is loaded from disk at
1006
+ runtime and was never scanned. Since cron runs non-interactively
1007
+ (auto-approves tool calls), a malicious skill carrying an injection
1008
+ payload bypassed every gate.
1009
+ """
1010
+ from tools.cronjob_tools import _scan_cron_prompt
1011
+
1012
+ scan_error = _scan_cron_prompt(assembled)
1013
+ if scan_error:
1014
+ job_label = job.get("name") or job.get("id") or "<unknown>"
1015
+ logger.warning(
1016
+ "Cron job '%s': assembled prompt blocked by injection scanner — %s",
1017
+ job_label,
1018
+ scan_error,
1019
+ )
1020
+ raise CronPromptInjectionBlocked(scan_error)
1021
+ return assembled
1022
+
1023
+
1024
+ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
1025
+ """
1026
+ Execute a single cron job.
1027
+
1028
+ Returns:
1029
+ Tuple of (success, full_output_doc, final_response, error_message)
1030
+ """
1031
+ job_id = job["id"]
1032
+ job_name = str(job.get("name") or job.get("prompt") or job_id or "cron job")
1033
+
1034
+ # ---------------------------------------------------------------
1035
+ # no_agent short-circuit — the script IS the job, no LLM involvement.
1036
+ # ---------------------------------------------------------------
1037
+ # This mirrors the classic "run a bash script on a timer, send its
1038
+ # stdout to telegram" watchdog pattern. The agent path is skipped
1039
+ # entirely: no AIAgent, no prompt, no tool loop, no token spend.
1040
+ #
1041
+ # We check this BEFORE importing run_agent / constructing SessionDB so
1042
+ # a pure-script tick never pays for the agent machinery it isn't going
1043
+ # to use. Keep this block self-contained.
1044
+ #
1045
+ # Semantics:
1046
+ # - script stdout (trimmed) → delivered verbatim as the final message
1047
+ # - empty stdout → silent run (no delivery, success=True)
1048
+ # - non-zero exit / timeout → delivered as an error alert, success=False
1049
+ # - wakeAgent=false gate → treated like empty stdout (silent), since
1050
+ # the whole point of no_agent is that there
1051
+ # is no agent to wake
1052
+ if job.get("no_agent"):
1053
+ script_path = job.get("script")
1054
+ if not script_path:
1055
+ err = "no_agent=True but no script is set for this job"
1056
+ logger.error("Job '%s': %s", job_id, err)
1057
+ return False, "", "", err
1058
+
1059
+ # Apply workdir if configured — lets scripts use predictable relative
1060
+ # paths. For no_agent jobs this is just the subprocess cwd (not an
1061
+ # agent TERMINAL_CWD bridge).
1062
+ _job_workdir = (job.get("workdir") or "").strip() or None
1063
+ _prior_cwd = None
1064
+ if _job_workdir and Path(_job_workdir).is_dir():
1065
+ _prior_cwd = os.getcwd()
1066
+ try:
1067
+ os.chdir(_job_workdir)
1068
+ except OSError:
1069
+ _prior_cwd = None
1070
+
1071
+ try:
1072
+ ok, output = _run_job_script(script_path)
1073
+ finally:
1074
+ if _prior_cwd is not None:
1075
+ try:
1076
+ os.chdir(_prior_cwd)
1077
+ except OSError:
1078
+ pass
1079
+
1080
+ now_iso = _hermes_now().strftime("%Y-%m-%d %H:%M:%S")
1081
+
1082
+ if not ok:
1083
+ # Script crashed / timed out / exited non-zero. Deliver the
1084
+ # error so the user knows the watchdog itself broke — silent
1085
+ # failure for an alerting job is the worst-case outcome.
1086
+ alert = (
1087
+ f"⚠ Cron watchdog '{job_name}' script failed\n\n"
1088
+ f"{output}\n\n"
1089
+ f"Time: {now_iso}"
1090
+ )
1091
+ doc = (
1092
+ f"# Cron Job: {job_name}\n\n"
1093
+ f"**Job ID:** {job_id}\n"
1094
+ f"**Run Time:** {now_iso}\n"
1095
+ f"**Mode:** no_agent (script)\n"
1096
+ f"**Status:** script failed\n\n"
1097
+ f"{output}\n"
1098
+ )
1099
+ return False, doc, alert, output
1100
+
1101
+ # Honour the wakeAgent gate as a silent signal — `wakeAgent: false`
1102
+ # means "nothing to report this tick", same as empty stdout.
1103
+ if not _parse_wake_gate(output):
1104
+ logger.info(
1105
+ "Job '%s' (no_agent): wakeAgent=false gate — silent run", job_id
1106
+ )
1107
+ silent_doc = (
1108
+ f"# Cron Job: {job_name}\n\n"
1109
+ f"**Job ID:** {job_id}\n"
1110
+ f"**Run Time:** {now_iso}\n"
1111
+ f"**Mode:** no_agent (script)\n"
1112
+ f"**Status:** silent (wakeAgent=false)\n"
1113
+ )
1114
+ return True, silent_doc, SILENT_MARKER, None
1115
+
1116
+ if not output.strip():
1117
+ logger.info("Job '%s' (no_agent): empty stdout — silent run", job_id)
1118
+ silent_doc = (
1119
+ f"# Cron Job: {job_name}\n\n"
1120
+ f"**Job ID:** {job_id}\n"
1121
+ f"**Run Time:** {now_iso}\n"
1122
+ f"**Mode:** no_agent (script)\n"
1123
+ f"**Status:** silent (empty output)\n"
1124
+ )
1125
+ return True, silent_doc, SILENT_MARKER, None
1126
+
1127
+ doc = (
1128
+ f"# Cron Job: {job_name}\n\n"
1129
+ f"**Job ID:** {job_id}\n"
1130
+ f"**Run Time:** {now_iso}\n"
1131
+ f"**Mode:** no_agent (script)\n\n"
1132
+ f"---\n\n"
1133
+ f"{output}\n"
1134
+ )
1135
+ return True, doc, output, None
1136
+
1137
+ # ---------------------------------------------------------------
1138
+ # Default (LLM) path — import and construct the agent machinery now
1139
+ # that we know we actually need it. Doing these imports here instead of
1140
+ # at module top keeps no_agent ticks from paying for AIAgent / SessionDB
1141
+ # construction costs.
1142
+ # ---------------------------------------------------------------
1143
+ from run_agent import AIAgent
1144
+
1145
+ # Initialize SQLite session store so cron job messages are persisted
1146
+ # and discoverable via session_search (same pattern as gateway/run.py).
1147
+ _session_db = None
1148
+ try:
1149
+ from calvyn_state import SessionDB
1150
+ _session_db = SessionDB()
1151
+ except Exception as e:
1152
+ logger.debug("Job '%s': SQLite session store not available: %s", job.get("id", "?"), e)
1153
+
1154
+ # Wake-gate: if this job has a pre-check script, run it BEFORE building
1155
+ # the prompt so a ``{"wakeAgent": false}`` response can short-circuit
1156
+ # the whole agent run. We pass the result into _build_job_prompt so
1157
+ # the script is only executed once.
1158
+ prerun_script = None
1159
+ script_path = job.get("script")
1160
+ if script_path:
1161
+ prerun_script = _run_job_script(script_path)
1162
+ _ran_ok, _script_output = prerun_script
1163
+ if _ran_ok and not _parse_wake_gate(_script_output):
1164
+ logger.info(
1165
+ "Job '%s' (ID: %s): wakeAgent=false, skipping agent run",
1166
+ job_name, job_id,
1167
+ )
1168
+ silent_doc = (
1169
+ f"# Cron Job: {job_name}\n\n"
1170
+ f"**Job ID:** {job_id}\n"
1171
+ f"**Run Time:** {_hermes_now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
1172
+ "Script gate returned `wakeAgent=false` — agent skipped.\n"
1173
+ )
1174
+ return True, silent_doc, SILENT_MARKER, None
1175
+
1176
+ try:
1177
+ prompt = _build_job_prompt(job, prerun_script=prerun_script)
1178
+ except CronPromptInjectionBlocked as block_exc:
1179
+ # Assembled prompt (user prompt + loaded skill content) tripped the
1180
+ # injection scanner. Refuse to run the agent this tick and surface
1181
+ # a clear failure to the operator so they see WHY the scheduled job
1182
+ # didn't run and can audit the offending skill.
1183
+ logger.warning(
1184
+ "Job '%s' (ID: %s): blocked by prompt-injection scanner — %s",
1185
+ job_name, job_id, block_exc,
1186
+ )
1187
+ blocked_doc = (
1188
+ f"# Cron Job: {job_name}\n\n"
1189
+ f"**Job ID:** {job_id}\n"
1190
+ f"**Run Time:** {_hermes_now().strftime('%Y-%m-%d %H:%M:%S')}\n"
1191
+ f"**Status:** BLOCKED\n\n"
1192
+ "The assembled prompt (user prompt + loaded skill content) tripped "
1193
+ "the cron injection scanner and the agent was NOT run.\n\n"
1194
+ f"**Scanner result:** {block_exc}\n\n"
1195
+ "Audit the skill(s) attached to this job for prompt-injection "
1196
+ "payloads or invisible-unicode markers. If the skill is legitimate "
1197
+ "and the match is a false positive, rephrase the content to avoid "
1198
+ "the threat pattern (`tools/cronjob_tools.py::_CRON_THREAT_PATTERNS`)."
1199
+ )
1200
+ return False, blocked_doc, "", str(block_exc)
1201
+ if prompt is None:
1202
+ logger.info("Job '%s': script produced no output, skipping AI call.", job_name)
1203
+ return True, "", SILENT_MARKER, None
1204
+ origin = _resolve_origin(job)
1205
+ _cron_session_id = f"cron_{job_id}_{_hermes_now().strftime('%Y%m%d_%H%M%S')}"
1206
+
1207
+ logger.info("Running job '%s' (ID: %s)", job_name, job_id)
1208
+ logger.info("Prompt: %s", prompt[:100])
1209
+
1210
+ agent = None
1211
+
1212
+ # Mark this as a cron session so the approval system can apply cron_mode.
1213
+ # This env var is process-wide and persists for the lifetime of the
1214
+ # scheduler process — every job this process runs is a cron job.
1215
+ os.environ["HERMES_CRON_SESSION"] = "1"
1216
+
1217
+ # Use ContextVars for per-job session/delivery state so parallel jobs
1218
+ # don't clobber each other's targets (os.environ is process-global).
1219
+ from gateway.session_context import set_session_vars, clear_session_vars, _VAR_MAP
1220
+
1221
+ # Cron execution is an internal scheduler context, not a live inbound
1222
+ # gateway message. Do not seed HERMES_SESSION_* contextvars from the
1223
+ # stored ``origin`` (which is delivery routing metadata, not a sender
1224
+ # identity). Several tool consumers branch on these vars during job
1225
+ # execution and would otherwise behave as if a real user from the
1226
+ # origin chat was driving the agent:
1227
+ # - tools/terminal_tool.py: background-process notification routing
1228
+ # (notify_on_complete / watch_patterns) reads HERMES_SESSION_PLATFORM
1229
+ # and HERMES_SESSION_CHAT_ID to populate watcher_platform / chat_id,
1230
+ # which would route completion notifications to the origin chat
1231
+ # instead of via HERMES_CRON_AUTO_DELIVER_* below.
1232
+ # - tools/tts_tool.py: picks Opus vs MP3 based on
1233
+ # HERMES_SESSION_PLATFORM == "telegram".
1234
+ # - tools/skills_tool.py + agent/prompt_builder.py: per-platform
1235
+ # skill-disable lists and the system-prompt cache key both consume
1236
+ # HERMES_SESSION_PLATFORM.
1237
+ # - tools/send_message_tool.py: mirror source labelling and the
1238
+ # send_message gate read HERMES_SESSION_PLATFORM.
1239
+ # Cron output delivery itself reads job["origin"] directly via
1240
+ # _resolve_origin(job) and the HERMES_CRON_AUTO_DELIVER_* vars set
1241
+ # below, so clearing HERMES_SESSION_* here does not affect delivery.
1242
+ _ctx_tokens = set_session_vars(
1243
+ platform="",
1244
+ chat_id="",
1245
+ chat_name="",
1246
+ )
1247
+ _cron_delivery_vars = (
1248
+ "HERMES_CRON_AUTO_DELIVER_PLATFORM",
1249
+ "HERMES_CRON_AUTO_DELIVER_CHAT_ID",
1250
+ "HERMES_CRON_AUTO_DELIVER_THREAD_ID",
1251
+ )
1252
+ for _var_name in _cron_delivery_vars:
1253
+ _VAR_MAP[_var_name].set("")
1254
+
1255
+ # Per-job working directory. When set (and validated at create/update
1256
+ # time), we point TERMINAL_CWD at it so:
1257
+ # - build_context_files_prompt() picks up AGENTS.md / CLAUDE.md /
1258
+ # .cursorrules from the job's project dir, AND
1259
+ # - the terminal, file, and code-exec tools run commands from there.
1260
+ #
1261
+ # tick() serializes workdir-jobs outside the parallel pool, so mutating
1262
+ # os.environ["TERMINAL_CWD"] here is safe for those jobs. For workdir-less
1263
+ # jobs we leave TERMINAL_CWD untouched — preserves the original behaviour
1264
+ # (skip_context_files=True, tools use whatever cwd the scheduler has).
1265
+ _job_workdir = (job.get("workdir") or "").strip() or None
1266
+ if _job_workdir and not Path(_job_workdir).is_dir():
1267
+ # Directory was removed between create-time validation and now. Log
1268
+ # and drop back to old behaviour rather than crashing the job.
1269
+ logger.warning(
1270
+ "Job '%s': configured workdir %r no longer exists — running without it",
1271
+ job_id, _job_workdir,
1272
+ )
1273
+ _job_workdir = None
1274
+ _prior_terminal_cwd = os.environ.get("TERMINAL_CWD", "_UNSET_")
1275
+ if _job_workdir:
1276
+ os.environ["TERMINAL_CWD"] = _job_workdir
1277
+ logger.info("Job '%s': using workdir %s", job_id, _job_workdir)
1278
+
1279
+ try:
1280
+ # Re-read .env and config.yaml fresh every run so provider/key
1281
+ # changes take effect without a gateway restart.
1282
+ from dotenv import load_dotenv
1283
+ try:
1284
+ load_dotenv(str(_get_hermes_home() / ".env"), override=True, encoding="utf-8")
1285
+ except UnicodeDecodeError:
1286
+ load_dotenv(str(_get_hermes_home() / ".env"), override=True, encoding="latin-1")
1287
+
1288
+ delivery_target = _resolve_delivery_target(job)
1289
+ if delivery_target:
1290
+ _VAR_MAP["HERMES_CRON_AUTO_DELIVER_PLATFORM"].set(delivery_target["platform"])
1291
+ _VAR_MAP["HERMES_CRON_AUTO_DELIVER_CHAT_ID"].set(str(delivery_target["chat_id"]))
1292
+ _VAR_MAP["HERMES_CRON_AUTO_DELIVER_THREAD_ID"].set(
1293
+ ""
1294
+ if delivery_target.get("thread_id") is None
1295
+ else str(delivery_target["thread_id"])
1296
+ )
1297
+
1298
+ model = job.get("model") or os.getenv("HERMES_MODEL") or ""
1299
+
1300
+ # Load config.yaml for model, reasoning, prefill, toolsets, provider routing
1301
+ _cfg = {}
1302
+ try:
1303
+ import yaml
1304
+ _cfg_path = str(_get_hermes_home() / "config.yaml")
1305
+ if os.path.exists(_cfg_path):
1306
+ with open(_cfg_path, encoding="utf-8") as _f:
1307
+ _cfg = yaml.safe_load(_f) or {}
1308
+ _cfg = _expand_env_vars(_cfg)
1309
+ _model_cfg = _cfg.get("model", {})
1310
+ if not job.get("model"):
1311
+ if isinstance(_model_cfg, str):
1312
+ model = _model_cfg
1313
+ elif isinstance(_model_cfg, dict):
1314
+ model = _model_cfg.get("default", model)
1315
+ except Exception as e:
1316
+ logger.warning("Job '%s': failed to load config.yaml, using defaults: %s", job_id, e)
1317
+
1318
+ # Apply IPv4 preference if configured.
1319
+ try:
1320
+ from calvyn_constants import apply_ipv4_preference
1321
+ _net_cfg = _cfg.get("network", {})
1322
+ if isinstance(_net_cfg, dict) and _net_cfg.get("force_ipv4"):
1323
+ apply_ipv4_preference(force=True)
1324
+ except Exception:
1325
+ pass
1326
+
1327
+ # Reasoning config from config.yaml
1328
+ from calvyn_constants import parse_reasoning_effort
1329
+ effort = str(_cfg.get("agent", {}).get("reasoning_effort", "")).strip()
1330
+ reasoning_config = parse_reasoning_effort(effort)
1331
+
1332
+ # Prefill messages from env or config.yaml
1333
+ prefill_messages = None
1334
+ prefill_file = os.getenv("HERMES_PREFILL_MESSAGES_FILE", "") or _cfg.get("prefill_messages_file", "")
1335
+ if prefill_file:
1336
+ pfpath = Path(prefill_file).expanduser()
1337
+ if not pfpath.is_absolute():
1338
+ pfpath = _get_hermes_home() / pfpath
1339
+ if pfpath.exists():
1340
+ try:
1341
+ with open(pfpath, "r", encoding="utf-8") as _pf:
1342
+ prefill_messages = json.load(_pf)
1343
+ if not isinstance(prefill_messages, list):
1344
+ prefill_messages = None
1345
+ except Exception as e:
1346
+ logger.warning("Job '%s': failed to parse prefill messages file '%s': %s", job_id, pfpath, e)
1347
+ prefill_messages = None
1348
+
1349
+ # Max iterations
1350
+ max_iterations = _cfg.get("agent", {}).get("max_turns") or _cfg.get("max_turns") or 90
1351
+
1352
+ # Provider routing
1353
+ pr = _cfg.get("provider_routing", {})
1354
+
1355
+ from hermes_cli.runtime_provider import (
1356
+ resolve_runtime_provider,
1357
+ format_runtime_provider_error,
1358
+ )
1359
+ from hermes_cli.auth import AuthError
1360
+ try:
1361
+ # Do not inject HERMES_INFERENCE_PROVIDER here. resolve_runtime_provider()
1362
+ # already prefers persisted config over stale shell/env overrides when
1363
+ # no explicit provider is requested. Passing the env var here short-
1364
+ # circuits that precedence and can resurrect old providers (for
1365
+ # example DeepSeek) for cron jobs that do not pin provider/model.
1366
+ runtime_kwargs = {
1367
+ "requested": job.get("provider"),
1368
+ }
1369
+ if job.get("base_url"):
1370
+ runtime_kwargs["explicit_base_url"] = job.get("base_url")
1371
+ runtime = resolve_runtime_provider(**runtime_kwargs)
1372
+ except AuthError as auth_exc:
1373
+ # Primary provider auth failed — try fallback chain before giving up.
1374
+ logger.warning("Job '%s': primary auth failed (%s), trying fallback", job_id, auth_exc)
1375
+ fb = _cfg.get("fallback_providers") or _cfg.get("fallback_model")
1376
+ fb_list = (fb if isinstance(fb, list) else [fb]) if fb else []
1377
+ runtime = None
1378
+ for entry in fb_list:
1379
+ if not isinstance(entry, dict):
1380
+ continue
1381
+ try:
1382
+ fb_kwargs = {"requested": entry.get("provider")}
1383
+ if entry.get("base_url"):
1384
+ fb_kwargs["explicit_base_url"] = entry["base_url"]
1385
+ if entry.get("api_key"):
1386
+ fb_kwargs["explicit_api_key"] = entry["api_key"]
1387
+ runtime = resolve_runtime_provider(**fb_kwargs)
1388
+ logger.info("Job '%s': fallback resolved to %s", job_id, runtime.get("provider"))
1389
+ break
1390
+ except Exception as fb_exc:
1391
+ logger.debug("Job '%s': fallback %s failed: %s", job_id, entry.get("provider"), fb_exc)
1392
+ if runtime is None:
1393
+ raise RuntimeError(format_runtime_provider_error(auth_exc)) from auth_exc
1394
+ except Exception as exc:
1395
+ message = format_runtime_provider_error(exc)
1396
+ raise RuntimeError(message) from exc
1397
+
1398
+ fallback_model = _cfg.get("fallback_providers") or _cfg.get("fallback_model") or None
1399
+ credential_pool = None
1400
+ runtime_provider = str(runtime.get("provider") or "").strip().lower()
1401
+ if runtime_provider:
1402
+ try:
1403
+ from agent.credential_pool import load_pool
1404
+ pool = load_pool(runtime_provider)
1405
+ if pool.has_credentials():
1406
+ credential_pool = pool
1407
+ logger.info(
1408
+ "Job '%s': loaded credential pool for provider %s with %d entries",
1409
+ job_id,
1410
+ runtime_provider,
1411
+ len(pool.entries()),
1412
+ )
1413
+ except Exception as e:
1414
+ logger.debug("Job '%s': failed to load credential pool for %s: %s", job_id, runtime_provider, e)
1415
+
1416
+ # Initialize MCP servers so configured mcp_servers are available to
1417
+ # the agent's tool registry before AIAgent is constructed. Without
1418
+ # this, cron jobs never saw any MCP tools — only the gateway / CLI
1419
+ # paths called discover_mcp_tools() at startup. Idempotent: subsequent
1420
+ # ticks short-circuit on already-connected servers inside
1421
+ # register_mcp_servers(). Non-fatal on failure: a broken MCP server
1422
+ # shouldn't kill an otherwise-working cron job. See #4219.
1423
+ try:
1424
+ from tools.mcp_tool import discover_mcp_tools
1425
+ _mcp_tools = discover_mcp_tools()
1426
+ if _mcp_tools:
1427
+ logger.info(
1428
+ "Job '%s': %d MCP tool(s) available",
1429
+ job_id, len(_mcp_tools),
1430
+ )
1431
+ except Exception as _mcp_exc:
1432
+ logger.warning(
1433
+ "Job '%s': MCP initialization failed (non-fatal): %s",
1434
+ job_id, _mcp_exc,
1435
+ )
1436
+
1437
+ agent = AIAgent(
1438
+ model=model,
1439
+ api_key=runtime.get("api_key"),
1440
+ base_url=runtime.get("base_url"),
1441
+ provider=runtime.get("provider"),
1442
+ api_mode=runtime.get("api_mode"),
1443
+ acp_command=runtime.get("command"),
1444
+ acp_args=runtime.get("args"),
1445
+ max_iterations=max_iterations,
1446
+ reasoning_config=reasoning_config,
1447
+ prefill_messages=prefill_messages,
1448
+ fallback_model=fallback_model,
1449
+ credential_pool=credential_pool,
1450
+ providers_allowed=pr.get("only"),
1451
+ providers_ignored=pr.get("ignore"),
1452
+ providers_order=pr.get("order"),
1453
+ provider_sort=pr.get("sort"),
1454
+ openrouter_min_coding_score=(_cfg.get("openrouter") or {}).get("min_coding_score"),
1455
+ enabled_toolsets=_resolve_cron_enabled_toolsets(job, _cfg),
1456
+ disabled_toolsets=["cronjob", "messaging", "clarify"],
1457
+ quiet_mode=True,
1458
+ # Cron jobs should always inherit the user's SOUL.md identity from
1459
+ # HERMES_HOME. When a workdir is configured, also inject project
1460
+ # context files (AGENTS.md / CLAUDE.md / .cursorrules) from there.
1461
+ # Without a workdir, keep cwd context discovery disabled.
1462
+ skip_context_files=not bool(_job_workdir),
1463
+ load_soul_identity=True,
1464
+ skip_memory=True, # Cron system prompts would corrupt user representations
1465
+ platform="cron",
1466
+ session_id=_cron_session_id,
1467
+ session_db=_session_db,
1468
+ )
1469
+
1470
+ # Run the agent with an *inactivity*-based timeout: the job can run
1471
+ # for hours if it's actively calling tools / receiving stream tokens,
1472
+ # but a hung API call or stuck tool with no activity for the configured
1473
+ # duration is caught and killed. Default 600s (10 min inactivity);
1474
+ # override via HERMES_CRON_TIMEOUT env var. 0 = unlimited.
1475
+ #
1476
+ # Uses the agent's built-in activity tracker (updated by
1477
+ # _touch_activity() on every tool call, API call, and stream delta).
1478
+ _raw_cron_timeout = os.getenv("HERMES_CRON_TIMEOUT", "").strip()
1479
+ if _raw_cron_timeout:
1480
+ try:
1481
+ _cron_timeout = float(_raw_cron_timeout)
1482
+ except (ValueError, TypeError):
1483
+ logger.warning(
1484
+ "Invalid HERMES_CRON_TIMEOUT=%r; using default 600s",
1485
+ _raw_cron_timeout,
1486
+ )
1487
+ _cron_timeout = 600.0
1488
+ else:
1489
+ _cron_timeout = 600.0
1490
+ _cron_inactivity_limit = _cron_timeout if _cron_timeout > 0 else None
1491
+ _POLL_INTERVAL = 5.0
1492
+ _cron_pool = concurrent.futures.ThreadPoolExecutor(max_workers=1)
1493
+ # Preserve scheduler-scoped ContextVar state (for example skill-declared
1494
+ # env passthrough registrations) when the cron run hops into the worker
1495
+ # thread used for inactivity timeout monitoring.
1496
+ _cron_context = contextvars.copy_context()
1497
+ _cron_future = _cron_pool.submit(_cron_context.run, agent.run_conversation, prompt)
1498
+ _inactivity_timeout = False
1499
+ try:
1500
+ if _cron_inactivity_limit is None:
1501
+ # Unlimited — just wait for the result.
1502
+ result = _cron_future.result()
1503
+ else:
1504
+ result = None
1505
+ while True:
1506
+ done, _ = concurrent.futures.wait(
1507
+ {_cron_future}, timeout=_POLL_INTERVAL,
1508
+ )
1509
+ if done:
1510
+ result = _cron_future.result()
1511
+ break
1512
+ # Agent still running — check inactivity.
1513
+ _idle_secs = 0.0
1514
+ if hasattr(agent, "get_activity_summary"):
1515
+ try:
1516
+ _act = agent.get_activity_summary()
1517
+ _idle_secs = _act.get("seconds_since_activity", 0.0)
1518
+ except Exception:
1519
+ pass
1520
+ if _idle_secs >= _cron_inactivity_limit:
1521
+ _inactivity_timeout = True
1522
+ break
1523
+ except Exception:
1524
+ _cron_pool.shutdown(wait=False, cancel_futures=True)
1525
+ raise
1526
+ finally:
1527
+ _cron_pool.shutdown(wait=False, cancel_futures=True)
1528
+
1529
+ if _inactivity_timeout:
1530
+ # Build diagnostic summary from the agent's activity tracker.
1531
+ _activity = {}
1532
+ if hasattr(agent, "get_activity_summary"):
1533
+ try:
1534
+ _activity = agent.get_activity_summary()
1535
+ except Exception:
1536
+ pass
1537
+ _last_desc = _activity.get("last_activity_desc", "unknown")
1538
+ _secs_ago = _activity.get("seconds_since_activity", 0)
1539
+ _cur_tool = _activity.get("current_tool")
1540
+ _iter_n = _activity.get("api_call_count", 0)
1541
+ _iter_max = _activity.get("max_iterations", 0)
1542
+
1543
+ logger.error(
1544
+ "Job '%s' idle for %.0fs (inactivity limit %.0fs) "
1545
+ "| last_activity=%s | iteration=%s/%s | tool=%s",
1546
+ job_name, _secs_ago, _cron_inactivity_limit,
1547
+ _last_desc, _iter_n, _iter_max,
1548
+ _cur_tool or "none",
1549
+ )
1550
+ if hasattr(agent, "interrupt"):
1551
+ agent.interrupt("Cron job timed out (inactivity)")
1552
+ raise TimeoutError(
1553
+ f"Cron job '{job_name}' idle for "
1554
+ f"{int(_secs_ago)}s (limit {int(_cron_inactivity_limit)}s) "
1555
+ f"— last activity: {_last_desc}"
1556
+ )
1557
+
1558
+ # Guard against non-dict returns from run_conversation under error conditions
1559
+ if not isinstance(result, dict):
1560
+ raise RuntimeError(
1561
+ f"agent.run_conversation returned {type(result).__name__} instead of dict: {result!r}"
1562
+ )
1563
+
1564
+ # If the agent itself reported failure (e.g. all retries exhausted on
1565
+ # API errors, model abort, mid-run interrupt), do not silently mark the
1566
+ # job as successful. run_agent populates `failed=True`/`completed=False`
1567
+ # on these paths and may put the error into `final_response`, which
1568
+ # would otherwise be delivered as if it were the agent's reply and the
1569
+ # job's `last_status` set to "ok". Raise so the except handler below
1570
+ # builds the proper failure tuple. (issue #17855)
1571
+ if result.get("failed") is True or result.get("completed") is False:
1572
+ _err_text = (
1573
+ result.get("error")
1574
+ or (result.get("final_response") or "").strip()
1575
+ or "agent reported failure"
1576
+ )
1577
+ raise RuntimeError(_err_text)
1578
+
1579
+ final_response = result.get("final_response", "") or ""
1580
+ # Strip leaked placeholder text that upstream may inject on empty completions.
1581
+ if final_response.strip() == "(No response generated)":
1582
+ final_response = ""
1583
+ # Use a separate variable for log display; keep final_response clean
1584
+ # for delivery logic (empty response = no delivery).
1585
+ logged_response = final_response if final_response else "(No response generated)"
1586
+
1587
+ output = f"""# Cron Job: {job_name}
1588
+
1589
+ **Job ID:** {job_id}
1590
+ **Run Time:** {_hermes_now().strftime('%Y-%m-%d %H:%M:%S')}
1591
+ **Schedule:** {job.get('schedule_display', 'N/A')}
1592
+
1593
+ ## Prompt
1594
+
1595
+ {prompt}
1596
+
1597
+ ## Response
1598
+
1599
+ {logged_response}
1600
+ """
1601
+
1602
+ logger.info("Job '%s' completed successfully", job_name)
1603
+ return True, output, final_response, None
1604
+
1605
+ except Exception as e:
1606
+ error_msg = f"{type(e).__name__}: {str(e)}"
1607
+ logger.exception("Job '%s' failed: %s", job_name, error_msg)
1608
+
1609
+ output = f"""# Cron Job: {job_name} (FAILED)
1610
+
1611
+ **Job ID:** {job_id}
1612
+ **Run Time:** {_hermes_now().strftime('%Y-%m-%d %H:%M:%S')}
1613
+ **Schedule:** {job.get('schedule_display', 'N/A')}
1614
+
1615
+ ## Prompt
1616
+
1617
+ {prompt}
1618
+
1619
+ ## Error
1620
+
1621
+ ```
1622
+ {error_msg}
1623
+ ```
1624
+ """
1625
+ return False, output, "", error_msg
1626
+
1627
+ finally:
1628
+ # Restore TERMINAL_CWD to whatever it was before this job ran. We
1629
+ # only ever mutate it when the job has a workdir; see the setup block
1630
+ # at the top of run_job for the serialization guarantee.
1631
+ if _job_workdir:
1632
+ if _prior_terminal_cwd == "_UNSET_":
1633
+ os.environ.pop("TERMINAL_CWD", None)
1634
+ else:
1635
+ os.environ["TERMINAL_CWD"] = _prior_terminal_cwd
1636
+ # Clean up ContextVar session/delivery state for this job.
1637
+ clear_session_vars(_ctx_tokens)
1638
+ for _var_name in _cron_delivery_vars:
1639
+ _VAR_MAP[_var_name].set("")
1640
+ if _session_db:
1641
+ try:
1642
+ _session_db.end_session(_cron_session_id, "cron_complete")
1643
+ except (Exception, KeyboardInterrupt) as e:
1644
+ logger.debug("Job '%s': failed to end session: %s", job_id, e)
1645
+ try:
1646
+ _session_db.close()
1647
+ except (Exception, KeyboardInterrupt) as e:
1648
+ logger.debug("Job '%s': failed to close SQLite session store: %s", job_id, e)
1649
+ # Release subprocesses, terminal sandboxes, browser daemons, and the
1650
+ # main OpenAI/httpx client held by this ephemeral cron agent. Without
1651
+ # this, a gateway that ticks cron every N minutes leaks fds per job
1652
+ # until it hits EMFILE (#10200 / "too many open files").
1653
+ try:
1654
+ if agent is not None:
1655
+ agent.close()
1656
+ except (Exception, KeyboardInterrupt) as e:
1657
+ logger.debug("Job '%s': failed to close agent resources: %s", job_id, e)
1658
+ # Each cron run spins up a short-lived worker thread whose event loop
1659
+ # dies as soon as the ``ThreadPoolExecutor`` shuts down. Any async
1660
+ # httpx clients cached under that loop are now unusable — reap them
1661
+ # so their transports don't accumulate in the process-global cache.
1662
+ try:
1663
+ from agent.auxiliary_client import cleanup_stale_async_clients
1664
+ cleanup_stale_async_clients()
1665
+ except Exception as e:
1666
+ logger.debug("Job '%s': failed to reap stale auxiliary clients: %s", job_id, e)
1667
+
1668
+
1669
+ def tick(verbose: bool = True, adapters=None, loop=None) -> int:
1670
+ """
1671
+ Check and run all due jobs.
1672
+
1673
+ Uses a file lock so only one tick runs at a time, even if the gateway's
1674
+ in-process ticker and a standalone daemon or manual tick overlap.
1675
+
1676
+ Args:
1677
+ verbose: Whether to print status messages
1678
+ adapters: Optional dict mapping Platform → live adapter (from gateway)
1679
+ loop: Optional asyncio event loop (from gateway) for live adapter sends
1680
+
1681
+ Returns:
1682
+ Number of jobs executed (0 if another tick is already running)
1683
+ """
1684
+ lock_dir, lock_file = _get_lock_paths()
1685
+ lock_dir.mkdir(parents=True, exist_ok=True)
1686
+
1687
+ # Cross-platform file locking: fcntl on Unix, msvcrt on Windows
1688
+ lock_fd = None
1689
+ try:
1690
+ lock_fd = open(lock_file, "w", encoding="utf-8")
1691
+ if fcntl:
1692
+ fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1693
+ elif msvcrt:
1694
+ msvcrt.locking(lock_fd.fileno(), msvcrt.LK_NBLCK, 1)
1695
+ except (OSError, IOError):
1696
+ logger.debug("Tick skipped — another instance holds the lock")
1697
+ if lock_fd is not None:
1698
+ lock_fd.close()
1699
+ return 0
1700
+
1701
+ try:
1702
+ due_jobs = get_due_jobs()
1703
+
1704
+ if verbose and not due_jobs:
1705
+ logger.info("%s - No jobs due", _hermes_now().strftime('%H:%M:%S'))
1706
+ return 0
1707
+
1708
+ if verbose:
1709
+ logger.info("%s - %s job(s) due", _hermes_now().strftime('%H:%M:%S'), len(due_jobs))
1710
+
1711
+ # Advance next_run_at for all recurring jobs FIRST, under the file lock,
1712
+ # before any execution begins. This preserves at-most-once semantics.
1713
+ for job in due_jobs:
1714
+ advance_next_run(job["id"])
1715
+
1716
+ # Resolve max parallel workers: env var > config.yaml > unbounded.
1717
+ # Set HERMES_CRON_MAX_PARALLEL=1 to restore old serial behaviour.
1718
+ _max_workers: Optional[int] = None
1719
+ try:
1720
+ _env_par = os.getenv("HERMES_CRON_MAX_PARALLEL", "").strip()
1721
+ if _env_par:
1722
+ _max_workers = int(_env_par) or None
1723
+ except (ValueError, TypeError):
1724
+ logger.warning("Invalid HERMES_CRON_MAX_PARALLEL value; defaulting to unbounded")
1725
+ if _max_workers is None:
1726
+ try:
1727
+ _ucfg = load_config() or {}
1728
+ _cfg_par = (
1729
+ _ucfg.get("cron", {}) if isinstance(_ucfg, dict) else {}
1730
+ ).get("max_parallel_jobs")
1731
+ if _cfg_par is not None:
1732
+ _max_workers = int(_cfg_par) or None
1733
+ except Exception:
1734
+ pass
1735
+
1736
+ if verbose:
1737
+ logger.info(
1738
+ "Running %d job(s) in parallel (max_workers=%s)",
1739
+ len(due_jobs),
1740
+ _max_workers if _max_workers else "unbounded",
1741
+ )
1742
+
1743
+ def _process_job(job: dict) -> bool:
1744
+ """Run one due job end-to-end: execute, save, deliver, mark."""
1745
+ try:
1746
+ success, output, final_response, error = run_job(job)
1747
+
1748
+ output_file = save_job_output(job["id"], output)
1749
+ if verbose:
1750
+ logger.info("Output saved to: %s", output_file)
1751
+
1752
+ # Deliver the final response to the origin/target chat.
1753
+ # If the agent responded with [SILENT], skip delivery (but
1754
+ # output is already saved above). Failed jobs always deliver.
1755
+ deliver_content = final_response if success else f"⚠️ Cron job '{job.get('name', job['id'])}' failed:\n{error}"
1756
+ should_deliver = bool(deliver_content)
1757
+ if should_deliver and success and SILENT_MARKER in deliver_content.strip().upper():
1758
+ logger.info("Job '%s': agent returned %s — skipping delivery", job["id"], SILENT_MARKER)
1759
+ should_deliver = False
1760
+
1761
+ delivery_error = None
1762
+ if should_deliver:
1763
+ try:
1764
+ delivery_error = _deliver_result(job, deliver_content, adapters=adapters, loop=loop)
1765
+ except Exception as de:
1766
+ delivery_error = str(de)
1767
+ logger.error("Delivery failed for job %s: %s", job["id"], de)
1768
+
1769
+ # Treat empty final_response as a soft failure so last_status
1770
+ # is not "ok" — the agent ran but produced nothing useful.
1771
+ # (issue #8585)
1772
+ if success and not final_response:
1773
+ success = False
1774
+ error = "Agent completed but produced empty response (model error, timeout, or misconfiguration)"
1775
+
1776
+ mark_job_run(job["id"], success, error, delivery_error=delivery_error)
1777
+ return True
1778
+
1779
+ except Exception as e:
1780
+ logger.error("Error processing job %s: %s", job['id'], e)
1781
+ mark_job_run(job["id"], False, str(e))
1782
+ return False
1783
+
1784
+ # Partition due jobs: those with a per-job workdir mutate
1785
+ # os.environ["TERMINAL_CWD"] inside run_job, which is process-global —
1786
+ # so they MUST run sequentially to avoid corrupting each other. Jobs
1787
+ # without a workdir leave env untouched and stay parallel-safe.
1788
+ workdir_jobs = [j for j in due_jobs if (j.get("workdir") or "").strip()]
1789
+ parallel_jobs = [j for j in due_jobs if not (j.get("workdir") or "").strip()]
1790
+
1791
+ _results: list = []
1792
+
1793
+ # Sequential pass for workdir jobs.
1794
+ for job in workdir_jobs:
1795
+ _ctx = contextvars.copy_context()
1796
+ _results.append(_ctx.run(_process_job, job))
1797
+
1798
+ # Parallel pass for the rest — same behaviour as before.
1799
+ if parallel_jobs:
1800
+ with concurrent.futures.ThreadPoolExecutor(max_workers=_max_workers) as _tick_pool:
1801
+ _futures = []
1802
+ for job in parallel_jobs:
1803
+ _ctx = contextvars.copy_context()
1804
+ _futures.append(_tick_pool.submit(_ctx.run, _process_job, job))
1805
+ _results.extend(f.result() for f in _futures)
1806
+
1807
+ # Best-effort sweep of MCP stdio subprocesses that survived their
1808
+ # session teardown during this tick. Runs AFTER every job has
1809
+ # finished so active sessions (including live user chats) are
1810
+ # never touched — only PIDs explicitly detected as orphans in
1811
+ # tools.mcp_tool._run_stdio's finally block are reaped.
1812
+ try:
1813
+ from tools.mcp_tool import _kill_orphaned_mcp_children
1814
+ _kill_orphaned_mcp_children()
1815
+ except Exception as _e:
1816
+ logger.debug("Post-tick MCP orphan cleanup failed: %s", _e)
1817
+
1818
+ return sum(_results)
1819
+ finally:
1820
+ if fcntl:
1821
+ fcntl.flock(lock_fd, fcntl.LOCK_UN)
1822
+ elif msvcrt:
1823
+ try:
1824
+ msvcrt.locking(lock_fd.fileno(), msvcrt.LK_UNLCK, 1)
1825
+ except (OSError, IOError):
1826
+ pass
1827
+ lock_fd.close()
1828
+
1829
+
1830
+ if __name__ == "__main__":
1831
+ tick(verbose=True)
1832
+