machinaos 0.0.78 → 0.0.80

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 (743) hide show
  1. package/.env.template +74 -5
  2. package/{workflows/AI Assistant_workflow-1778504793388-ou1m1tz2x.json → .machina/workflows/AI Assistant_example_workflow-1779017037684-e2e5da7a.json } +164 -105
  3. package/{workflows/AI Employee_example_workflow-1777720598005-u4cm858dv.json → .machina/workflows/AI Employee_example_workflow-1779102911870-cbc76c82.json } +582 -328
  4. package/{workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json → .machina/workflows/Claude Assistant_example_workflow-1779095939967-2369cff4.json } +152 -83
  5. package/README.md +5 -2
  6. package/bin/cli.js +2 -2
  7. package/{machina → cli}/__main__.py +11 -7
  8. package/cli/_common.py +122 -0
  9. package/cli/buildenv.py +40 -0
  10. package/cli/cli.py +204 -0
  11. package/{machina → cli}/colors.py +10 -2
  12. package/cli/commands/__init__.py +1 -0
  13. package/cli/commands/_temporal_specs.py +59 -0
  14. package/{machina → cli}/commands/build.py +35 -45
  15. package/cli/commands/clean.py +141 -0
  16. package/cli/commands/daemon/__init__.py +47 -0
  17. package/cli/commands/daemon/_state.py +97 -0
  18. package/cli/commands/daemon/restart.py +14 -0
  19. package/cli/commands/daemon/start.py +49 -0
  20. package/cli/commands/daemon/status.py +20 -0
  21. package/cli/commands/daemon/stop.py +22 -0
  22. package/{machina → cli}/commands/dev.py +32 -42
  23. package/{machina → cli}/commands/docs.py +13 -11
  24. package/{machina → cli}/commands/start.py +69 -62
  25. package/{machina → cli}/commands/stop.py +7 -10
  26. package/{machina → cli}/commands/version.py +12 -6
  27. package/cli/config.py +170 -0
  28. package/cli/platform_.py +169 -0
  29. package/{machina → cli}/ports.py +42 -3
  30. package/{machina → cli}/run.py +29 -2
  31. package/{machina → cli}/supervisor.py +29 -12
  32. package/{machina → cli}/tcp.py +6 -2
  33. package/{machina → cli}/tree.py +38 -11
  34. package/client/dist/assets/{ActionBar-Du2MSFSz.js → ActionBar-Cjr3TF7g.js} +1 -1
  35. package/client/dist/assets/{ApiKeyInput-k2LBmBjb.js → ApiKeyInput-DIJE2PVA.js} +1 -1
  36. package/client/dist/assets/{ApiKeyPanel-C_bV9U0X.js → ApiKeyPanel-CPmye7uh.js} +1 -1
  37. package/client/dist/assets/{ApiUsageSection-CmVfwZzL.js → ApiUsageSection-TF_7gH2D.js} +1 -1
  38. package/client/dist/assets/{EmailPanel-CeKIMGu-.js → EmailPanel-Bs-xvbKR.js} +1 -1
  39. package/client/dist/assets/{OAuthPanel-KA3t3Q2K.js → OAuthPanel-BDtVJhAV.js} +1 -1
  40. package/client/dist/assets/{QrPairingPanel-NgNpJNuk.js → QrPairingPanel-BwJehTuZ.js} +1 -1
  41. package/client/dist/assets/{RateLimitSection-Du5YNVIA.js → RateLimitSection-CfNOoPIS.js} +1 -1
  42. package/client/dist/assets/{StatusCard-DNLyayXc.js → StatusCard-DkwIrgdP.js} +1 -1
  43. package/client/dist/assets/index-P2FzntoL.js +165 -0
  44. package/client/dist/index.html +1 -1
  45. package/client/package.json +1 -1
  46. package/client/src/Dashboard.tsx +128 -76
  47. package/client/src/adapters/nodeSpecToDescription.ts +7 -0
  48. package/client/src/assets/icons/index.test.ts +10 -0
  49. package/client/src/assets/icons/index.ts +16 -3
  50. package/client/src/components/AIAgentNode.tsx +8 -8
  51. package/client/src/components/ParameterRenderer.tsx +6 -3
  52. package/client/src/components/SkillEditorModal.tsx +1 -0
  53. package/client/src/components/credentials/panels/EmailPanel.tsx +2 -0
  54. package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +2 -0
  55. package/client/src/components/credentials/sections/RateLimitSection.tsx +1 -0
  56. package/client/src/components/icons/AIProviderIcons.tsx +1 -0
  57. package/client/src/components/maps/GoogleMapsPicker.tsx +1 -0
  58. package/client/src/components/parameterPanel/InputSection.tsx +1 -0
  59. package/client/src/components/parameterPanel/MasterSkillEditor.tsx +1 -0
  60. package/client/src/components/parameterPanel/OutputSection.tsx +1 -0
  61. package/client/src/components/ui/ComponentPalette.tsx +1 -0
  62. package/client/src/components/ui/MapSelector.tsx +1 -0
  63. package/client/src/components/ui/NodeContextMenu.tsx +3 -3
  64. package/client/src/components/ui/SettingsPanel.tsx +1 -0
  65. package/client/src/components/ui/action-button.tsx +1 -0
  66. package/client/src/components/ui/badge.tsx +1 -0
  67. package/client/src/components/ui/button.tsx +1 -0
  68. package/client/src/components/ui/form.tsx +1 -0
  69. package/client/src/components/ui/tabs.tsx +1 -0
  70. package/client/src/contexts/AuthContext.tsx +1 -0
  71. package/client/src/contexts/ThemeContext.tsx +1 -0
  72. package/client/src/contexts/WebSocketContext.tsx +104 -34
  73. package/client/src/hooks/__tests__/useApiKeys.test.ts +2 -2
  74. package/client/src/hooks/useReactFlowNodes.ts +1 -0
  75. package/client/src/hooks/useWorkflowValidation.ts +142 -0
  76. package/client/src/lib/nodeSpec.ts +1 -0
  77. package/client/src/test/providers.tsx +1 -0
  78. package/client/src/types/__tests__/cloudEvents.test.ts +5 -2
  79. package/client/src/types/cloudEvents.ts +19 -7
  80. package/client/src/utils/nodeUtils.ts +1 -1
  81. package/client/src/utils/workflow.ts +8 -2
  82. package/client/src/utils/workflowExport.ts +60 -3
  83. package/package.json +24 -23
  84. package/scripts/install.js +16 -27
  85. package/scripts/migrate_icons.py +3 -1
  86. package/scripts/migrate_skill_icons.py +6 -7
  87. package/scripts/postinstall.js +11 -9
  88. package/server/config/ai_cli_providers.json +2 -3
  89. package/server/config/credential_providers.json +15 -15
  90. package/server/config/llm_defaults.json +1 -1
  91. package/server/config/model_registry.json +416 -611
  92. package/server/constants.py +285 -223
  93. package/server/core/__init__.py +1 -1
  94. package/server/core/cache.py +9 -29
  95. package/server/core/cleanup.py +12 -24
  96. package/server/core/config.py +148 -24
  97. package/server/core/container.py +68 -59
  98. package/server/core/credential_backends.py +5 -13
  99. package/server/core/credentials_database.py +13 -43
  100. package/server/core/database.py +292 -353
  101. package/server/core/health.py +4 -5
  102. package/server/core/logging.py +241 -87
  103. package/server/core/paths.py +285 -0
  104. package/server/core/tracing.py +2 -8
  105. package/server/gunicorn.conf.py +1 -0
  106. package/server/main.py +150 -74
  107. package/server/middleware/auth.py +18 -24
  108. package/server/models/__init__.py +1 -1
  109. package/server/models/auth.py +5 -12
  110. package/server/models/database.py +36 -68
  111. package/server/models/node_metadata.py +25 -18
  112. package/server/nodejs/dist/index.js +107 -0
  113. package/server/nodes/README.md +11 -5
  114. package/server/nodes/__init__.py +1 -1
  115. package/server/nodes/_visuals.py +146 -14
  116. package/server/nodes/agent/_events.py +124 -0
  117. package/server/nodes/agent/_handles.py +15 -29
  118. package/server/nodes/agent/_inline.py +28 -25
  119. package/server/nodes/agent/_specialized.py +30 -15
  120. package/server/nodes/agent/{ai_agent.py → ai_agent/__init__.py} +33 -17
  121. package/server/nodes/agent/ai_agent/meta.json +3 -0
  122. package/server/nodes/agent/{ai_employee.py → ai_employee/__init__.py} +5 -2
  123. package/server/nodes/agent/ai_employee/meta.json +3 -0
  124. package/server/nodes/agent/{android_agent.py → android_agent/__init__.py} +1 -1
  125. package/server/nodes/agent/android_agent/meta.json +3 -0
  126. package/server/nodes/agent/{autonomous_agent.py → autonomous_agent/__init__.py} +2 -1
  127. package/server/nodes/agent/autonomous_agent/meta.json +3 -0
  128. package/server/nodes/agent/{chat_agent.py → chat_agent/__init__.py} +29 -12
  129. package/server/nodes/agent/chat_agent/meta.json +3 -0
  130. package/server/nodes/agent/{claude_code_agent.py → claude_code_agent/__init__.py} +192 -95
  131. package/server/nodes/agent/claude_code_agent/_handlers.py +169 -0
  132. package/server/{services/claude_oauth.py → nodes/agent/claude_code_agent/_oauth.py} +26 -13
  133. package/server/nodes/agent/claude_code_agent/_pool.py +1020 -0
  134. package/server/nodes/agent/claude_code_agent/_provider.py +513 -0
  135. package/server/nodes/agent/claude_code_agent/_skills.py +245 -0
  136. package/server/nodes/agent/claude_code_agent/meta.json +3 -0
  137. package/server/nodes/agent/{codex_agent.py → codex_agent/__init__.py} +26 -35
  138. package/server/nodes/agent/codex_agent/meta.json +3 -0
  139. package/server/nodes/agent/{coding_agent.py → coding_agent/__init__.py} +1 -1
  140. package/server/nodes/agent/coding_agent/meta.json +3 -0
  141. package/server/nodes/agent/{consumer_agent.py → consumer_agent/__init__.py} +1 -1
  142. package/server/nodes/agent/consumer_agent/meta.json +3 -0
  143. package/server/nodes/agent/{orchestrator_agent.py → orchestrator_agent/__init__.py} +5 -2
  144. package/server/nodes/agent/orchestrator_agent/meta.json +3 -0
  145. package/server/nodes/agent/{payments_agent.py → payments_agent/__init__.py} +1 -1
  146. package/server/nodes/agent/payments_agent/meta.json +3 -0
  147. package/server/nodes/agent/{productivity_agent.py → productivity_agent/__init__.py} +1 -1
  148. package/server/nodes/agent/productivity_agent/meta.json +3 -0
  149. package/server/nodes/agent/{rlm_agent.py → rlm_agent/__init__.py} +18 -17
  150. package/server/nodes/agent/rlm_agent/meta.json +3 -0
  151. package/server/nodes/agent/{social_agent.py → social_agent/__init__.py} +1 -1
  152. package/server/nodes/agent/social_agent/meta.json +3 -0
  153. package/server/nodes/agent/{task_agent.py → task_agent/__init__.py} +1 -1
  154. package/server/nodes/agent/task_agent/meta.json +3 -0
  155. package/server/nodes/agent/{tool_agent.py → tool_agent/__init__.py} +1 -1
  156. package/server/nodes/agent/tool_agent/meta.json +3 -0
  157. package/server/nodes/agent/{travel_agent.py → travel_agent/__init__.py} +1 -1
  158. package/server/nodes/agent/travel_agent/meta.json +3 -0
  159. package/server/nodes/agent/{web_agent.py → web_agent/__init__.py} +1 -1
  160. package/server/nodes/agent/web_agent/meta.json +3 -0
  161. package/server/nodes/android/__init__.py +24 -0
  162. package/server/nodes/android/_base.py +93 -76
  163. package/server/nodes/android/_dispatcher.py +140 -223
  164. package/server/nodes/android/_events.py +154 -0
  165. package/server/nodes/android/_handlers.py +13 -7
  166. package/server/nodes/android/_option_loaders.py +1 -4
  167. package/server/nodes/android/_refresh.py +27 -37
  168. package/server/nodes/android/_relay/broadcaster.py +25 -41
  169. package/server/nodes/android/_relay/client.py +23 -42
  170. package/server/nodes/android/_relay/manager.py +1 -0
  171. package/server/nodes/android/_relay/protocol.py +6 -0
  172. package/server/nodes/android/_router.py +48 -133
  173. package/server/nodes/android/{airplane_mode_control.py → airplane_mode_control/__init__.py} +2 -1
  174. package/server/nodes/android/airplane_mode_control/meta.json +3 -0
  175. package/server/nodes/android/{app_launcher.py → app_launcher/__init__.py} +2 -1
  176. package/server/nodes/android/app_launcher/meta.json +3 -0
  177. package/server/nodes/android/{app_list.py → app_list/__init__.py} +2 -1
  178. package/server/nodes/android/app_list/meta.json +3 -0
  179. package/server/nodes/android/{audio_automation.py → audio_automation/__init__.py} +2 -1
  180. package/server/nodes/android/audio_automation/meta.json +3 -0
  181. package/server/nodes/android/{battery_monitor.py → battery_monitor/__init__.py} +2 -1
  182. package/server/nodes/android/battery_monitor/meta.json +3 -0
  183. package/server/nodes/android/{bluetooth_automation.py → bluetooth_automation/__init__.py} +2 -1
  184. package/server/nodes/android/bluetooth_automation/meta.json +3 -0
  185. package/server/nodes/android/{camera_control.py → camera_control/__init__.py} +2 -1
  186. package/server/nodes/android/camera_control/meta.json +3 -0
  187. package/server/nodes/android/{device_state_automation.py → device_state_automation/__init__.py} +2 -1
  188. package/server/nodes/android/device_state_automation/meta.json +3 -0
  189. package/server/nodes/android/{environmental_sensors.py → environmental_sensors/__init__.py} +2 -1
  190. package/server/nodes/android/environmental_sensors/meta.json +3 -0
  191. package/server/nodes/android/{location.py → location/__init__.py} +2 -1
  192. package/server/nodes/android/location/meta.json +3 -0
  193. package/server/nodes/android/{media_control.py → media_control/__init__.py} +2 -1
  194. package/server/nodes/android/media_control/meta.json +3 -0
  195. package/server/nodes/android/{motion_detection.py → motion_detection/__init__.py} +2 -1
  196. package/server/nodes/android/motion_detection/meta.json +3 -0
  197. package/server/nodes/android/{network_monitor.py → network_monitor/__init__.py} +2 -1
  198. package/server/nodes/android/network_monitor/meta.json +3 -0
  199. package/server/nodes/android/{screen_control_automation.py → screen_control_automation/__init__.py} +2 -1
  200. package/server/nodes/android/screen_control_automation/meta.json +3 -0
  201. package/server/nodes/android/{system_info.py → system_info/__init__.py} +2 -1
  202. package/server/nodes/android/system_info/meta.json +3 -0
  203. package/server/nodes/android/{wifi_automation.py → wifi_automation/__init__.py} +2 -1
  204. package/server/nodes/android/wifi_automation/meta.json +3 -0
  205. package/server/nodes/browser/__init__.py +22 -1
  206. package/server/nodes/browser/_install.py +63 -0
  207. package/server/nodes/browser/_service.py +21 -25
  208. package/server/nodes/browser/{browser.py → browser/__init__.py} +58 -25
  209. package/server/nodes/browser/browser/meta.json +3 -0
  210. package/server/nodes/chat/{chat_history.py → chat_history/__init__.py} +2 -4
  211. package/server/nodes/chat/chat_history/meta.json +3 -0
  212. package/server/nodes/chat/{chat_send.py → chat_send/__init__.py} +2 -4
  213. package/server/nodes/chat/chat_send/icon.svg +1 -0
  214. package/server/nodes/chat/chat_send/meta.json +3 -0
  215. package/server/nodes/code/_base.py +1 -1
  216. package/server/nodes/code/{javascript_executor.py → javascript_executor/__init__.py} +5 -5
  217. package/server/nodes/code/javascript_executor/meta.json +3 -0
  218. package/server/nodes/code/{python_executor.py → python_executor/__init__.py} +32 -14
  219. package/server/nodes/code/python_executor/meta.json +3 -0
  220. package/server/nodes/code/{typescript_executor.py → typescript_executor/__init__.py} +5 -5
  221. package/server/nodes/code/typescript_executor/meta.json +3 -0
  222. package/server/nodes/document/{document_parser.py → document_parser/__init__.py} +26 -15
  223. package/server/nodes/document/document_parser/meta.json +3 -0
  224. package/server/nodes/document/{embedding_generator.py → embedding_generator/__init__.py} +16 -9
  225. package/server/nodes/document/embedding_generator/meta.json +3 -0
  226. package/server/nodes/document/{file_downloader.py → file_downloader/__init__.py} +30 -20
  227. package/server/nodes/document/file_downloader/meta.json +3 -0
  228. package/server/nodes/document/{http_scraper.py → http_scraper/__init__.py} +31 -21
  229. package/server/nodes/document/http_scraper/meta.json +3 -0
  230. package/server/nodes/document/{text_chunker.py → text_chunker/__init__.py} +17 -12
  231. package/server/nodes/document/text_chunker/meta.json +3 -0
  232. package/server/nodes/document/{vector_store.py → vector_store/__init__.py} +88 -72
  233. package/server/nodes/document/vector_store/meta.json +3 -0
  234. package/server/nodes/email/__init__.py +9 -2
  235. package/server/nodes/email/_events.py +54 -0
  236. package/server/nodes/email/_filters.py +3 -3
  237. package/server/nodes/email/_himalaya.py +95 -50
  238. package/server/nodes/email/_service.py +23 -13
  239. package/server/nodes/email/{email_read.py → email_read/__init__.py} +23 -11
  240. package/server/nodes/email/email_read/icon.svg +6 -0
  241. package/server/nodes/email/email_read/meta.json +3 -0
  242. package/server/nodes/email/{email_receive.py → email_receive/__init__.py} +45 -23
  243. package/server/nodes/email/email_receive/meta.json +3 -0
  244. package/server/nodes/email/{email_send.py → email_send/__init__.py} +13 -7
  245. package/server/nodes/email/email_send/meta.json +3 -0
  246. package/server/nodes/filesystem/_backend.py +1 -5
  247. package/server/nodes/filesystem/{file_modify.py → file_modify/__init__.py} +10 -5
  248. package/server/nodes/filesystem/file_modify/meta.json +3 -0
  249. package/server/nodes/filesystem/{file_read.py → file_read/__init__.py} +7 -3
  250. package/server/nodes/filesystem/file_read/meta.json +3 -0
  251. package/server/nodes/filesystem/{fs_search.py → fs_search/__init__.py} +11 -3
  252. package/server/nodes/filesystem/fs_search/meta.json +3 -0
  253. package/server/nodes/filesystem/{shell.py → shell/__init__.py} +12 -5
  254. package/server/nodes/filesystem/shell/meta.json +3 -0
  255. package/server/nodes/google/__init__.py +12 -0
  256. package/server/nodes/google/_auth_helper.py +7 -13
  257. package/server/nodes/google/_base.py +14 -11
  258. package/server/nodes/google/_credentials.py +2 -1
  259. package/server/nodes/google/_events.py +47 -0
  260. package/server/nodes/google/_filters.py +3 -3
  261. package/server/nodes/google/_gmail.py +70 -47
  262. package/server/nodes/google/_handlers.py +3 -1
  263. package/server/nodes/google/_oauth.py +25 -11
  264. package/server/nodes/google/_option_loaders.py +9 -30
  265. package/server/nodes/google/_refresh.py +8 -12
  266. package/server/nodes/google/_router.py +4 -5
  267. package/server/nodes/google/{calendar.py → calendar/__init__.py} +87 -64
  268. package/server/nodes/google/calendar/meta.json +3 -0
  269. package/server/nodes/google/{contacts.py → contacts/__init__.py} +84 -72
  270. package/server/nodes/google/contacts/meta.json +3 -0
  271. package/server/nodes/google/{drive.py → drive/__init__.py} +87 -72
  272. package/server/nodes/google/drive/meta.json +3 -0
  273. package/server/nodes/google/{gmail.py → gmail/__init__.py} +73 -39
  274. package/server/nodes/google/gmail/meta.json +3 -0
  275. package/server/nodes/google/{gmail_receive.py → gmail_receive/__init__.py} +31 -24
  276. package/server/nodes/google/gmail_receive/icon.svg +7 -0
  277. package/server/nodes/google/gmail_receive/meta.json +3 -0
  278. package/server/nodes/google/google.svg +7 -0
  279. package/server/nodes/google/{sheets.py → sheets/__init__.py} +54 -42
  280. package/server/nodes/google/sheets/meta.json +3 -0
  281. package/server/nodes/google/{tasks.py → tasks/__init__.py} +56 -43
  282. package/server/nodes/google/tasks/meta.json +3 -0
  283. package/server/nodes/groups.py +28 -28
  284. package/server/nodes/location/__init__.py +31 -1
  285. package/server/nodes/location/_credentials.py +1 -6
  286. package/server/nodes/location/_service.py +88 -107
  287. package/server/nodes/location/{gmaps_create.py → gmaps_create/__init__.py} +6 -6
  288. package/server/nodes/location/gmaps_create/meta.json +3 -0
  289. package/server/nodes/location/{gmaps_locations.py → gmaps_locations/__init__.py} +8 -6
  290. package/server/nodes/location/gmaps_locations/meta.json +3 -0
  291. package/server/nodes/location/{gmaps_nearby_places.py → gmaps_nearby_places/__init__.py} +8 -6
  292. package/server/nodes/location/gmaps_nearby_places/meta.json +3 -0
  293. package/server/nodes/model/_base.py +10 -7
  294. package/server/nodes/model/_credentials.py +10 -10
  295. package/server/nodes/model/_local_validator.py +28 -24
  296. package/server/nodes/model/{anthropic_chat_model.py → anthropic_chat_model/__init__.py} +5 -3
  297. package/server/nodes/model/anthropic_chat_model/meta.json +3 -0
  298. package/server/nodes/model/{cerebras_chat_model.py → cerebras_chat_model/__init__.py} +5 -3
  299. package/server/nodes/model/cerebras_chat_model/meta.json +3 -0
  300. package/server/nodes/model/{deepseek_chat_model.py → deepseek_chat_model/__init__.py} +8 -4
  301. package/server/nodes/model/deepseek_chat_model/meta.json +3 -0
  302. package/server/nodes/model/{gemini_chat_model.py → gemini_chat_model/__init__.py} +5 -3
  303. package/server/nodes/model/gemini_chat_model/meta.json +3 -0
  304. package/server/nodes/model/{groq_chat_model.py → groq_chat_model/__init__.py} +2 -2
  305. package/server/nodes/model/groq_chat_model/meta.json +3 -0
  306. package/server/nodes/model/{kimi_chat_model.py → kimi_chat_model/__init__.py} +2 -2
  307. package/server/nodes/model/kimi_chat_model/meta.json +3 -0
  308. package/server/nodes/model/{lmstudio_chat_model.py → lmstudio_chat_model/__init__.py} +2 -2
  309. package/server/nodes/model/lmstudio_chat_model/meta.json +3 -0
  310. package/server/nodes/model/{mistral_chat_model.py → mistral_chat_model/__init__.py} +2 -2
  311. package/server/nodes/model/mistral_chat_model/meta.json +3 -0
  312. package/server/nodes/model/{ollama_chat_model.py → ollama_chat_model/__init__.py} +2 -2
  313. package/server/nodes/model/ollama_chat_model/meta.json +3 -0
  314. package/server/nodes/model/{openai_chat_model.py → openai_chat_model/__init__.py} +8 -4
  315. package/server/nodes/model/openai_chat_model/meta.json +3 -0
  316. package/server/nodes/model/{openrouter_chat_model.py → openrouter_chat_model/__init__.py} +8 -4
  317. package/server/nodes/model/openrouter_chat_model/meta.json +3 -0
  318. package/server/nodes/proxy/_usage.py +14 -15
  319. package/server/nodes/proxy/{proxy_config.py → proxy_config/__init__.py} +39 -30
  320. package/server/nodes/proxy/proxy_config/meta.json +3 -0
  321. package/server/nodes/proxy/{proxy_request.py → proxy_request/__init__.py} +30 -16
  322. package/server/nodes/proxy/proxy_request/meta.json +3 -0
  323. package/server/nodes/proxy/{proxy_status.py → proxy_status/__init__.py} +2 -0
  324. package/server/nodes/proxy/proxy_status/meta.json +3 -0
  325. package/server/nodes/scheduler/{cron_scheduler.py → cron_scheduler/__init__.py} +96 -23
  326. package/server/nodes/scheduler/cron_scheduler/_workflow.py +155 -0
  327. package/server/nodes/scheduler/cron_scheduler/meta.json +3 -0
  328. package/server/nodes/scheduler/{timer.py → timer/__init__.py} +6 -5
  329. package/server/nodes/scheduler/timer/meta.json +3 -0
  330. package/server/nodes/scraper/_credentials.py +0 -1
  331. package/server/nodes/scraper/{apify_actor.py → apify_actor/__init__.py} +44 -35
  332. package/server/nodes/scraper/apify_actor/icon.svg +5 -0
  333. package/server/nodes/scraper/apify_actor/meta.json +3 -0
  334. package/server/nodes/scraper/{crawlee_scraper.py → crawlee_scraper/__init__.py} +96 -57
  335. package/server/nodes/scraper/crawlee_scraper/meta.json +3 -0
  336. package/server/nodes/search/{brave_search.py → brave_search/__init__.py} +6 -5
  337. package/server/nodes/search/brave_search/icon.svg +3 -0
  338. package/server/nodes/search/brave_search/meta.json +3 -0
  339. package/server/nodes/search/{duckduckgo_search.py → duckduckgo_search/__init__.py} +17 -6
  340. package/server/nodes/search/duckduckgo_search/meta.json +3 -0
  341. package/server/nodes/search/{perplexity_search.py → perplexity_search/__init__.py} +4 -5
  342. package/server/nodes/search/perplexity_search/icon.svg +3 -0
  343. package/server/nodes/search/perplexity_search/meta.json +3 -0
  344. package/server/nodes/search/{serper_search.py → serper_search/__init__.py} +32 -25
  345. package/server/nodes/search/serper_search/icon.svg +3 -0
  346. package/server/nodes/search/serper_search/meta.json +3 -0
  347. package/server/nodes/skill/__init__.py +21 -1
  348. package/server/nodes/skill/_expander.py +75 -0
  349. package/server/nodes/skill/{master_skill.py → master_skill/__init__.py} +2 -8
  350. package/server/nodes/skill/master_skill/_events.py +84 -0
  351. package/server/nodes/skill/master_skill/meta.json +3 -0
  352. package/server/nodes/skill/{simple_memory.py → simple_memory/__init__.py} +8 -16
  353. package/server/nodes/skill/simple_memory/meta.json +3 -0
  354. package/server/nodes/social/_base.py +223 -231
  355. package/server/nodes/social/{social_receive.py → social_receive/__init__.py} +38 -13
  356. package/server/nodes/social/social_receive/meta.json +3 -0
  357. package/server/nodes/social/{social_send.py → social_send/__init__.py} +71 -29
  358. package/server/nodes/social/social_send/icon.svg +1 -0
  359. package/server/nodes/social/social_send/meta.json +3 -0
  360. package/server/nodes/stripe/__init__.py +7 -3
  361. package/server/nodes/stripe/_credentials.py +0 -1
  362. package/server/nodes/stripe/_handlers.py +18 -7
  363. package/server/nodes/stripe/_install.py +14 -15
  364. package/server/nodes/stripe/_source.py +5 -5
  365. package/server/nodes/stripe/icon.svg +1 -0
  366. package/server/nodes/stripe/meta.json +3 -0
  367. package/server/nodes/stripe/stripe_action.py +4 -4
  368. package/server/nodes/stripe/stripe_receive.py +6 -9
  369. package/server/nodes/telegram/__init__.py +13 -0
  370. package/server/nodes/telegram/_credentials.py +2 -7
  371. package/server/nodes/telegram/_events.py +167 -0
  372. package/server/nodes/telegram/_filters.py +3 -11
  373. package/server/nodes/telegram/_handlers.py +17 -7
  374. package/server/nodes/telegram/_refresh.py +24 -34
  375. package/server/nodes/telegram/_service.py +29 -45
  376. package/server/nodes/telegram/meta.json +3 -0
  377. package/server/nodes/telegram/telegram.svg +3 -0
  378. package/server/nodes/telegram/telegram_receive.py +38 -18
  379. package/server/nodes/telegram/telegram_send.py +21 -19
  380. package/server/nodes/text/{file_handler.py → file_handler/__init__.py} +7 -1
  381. package/server/nodes/text/file_handler/meta.json +3 -0
  382. package/server/nodes/text/{text_generator.py → text_generator/__init__.py} +2 -1
  383. package/server/nodes/text/text_generator/meta.json +3 -0
  384. package/server/nodes/tool/{agent_builder.py → agent_builder/__init__.py} +105 -100
  385. package/server/nodes/tool/agent_builder/_events.py +91 -0
  386. package/server/nodes/tool/agent_builder/meta.json +3 -0
  387. package/server/nodes/tool/{calculator_tool.py → calculator_tool/__init__.py} +19 -7
  388. package/server/nodes/tool/calculator_tool/meta.json +3 -0
  389. package/server/nodes/tool/{current_time_tool.py → current_time_tool/__init__.py} +6 -4
  390. package/server/nodes/tool/current_time_tool/meta.json +3 -0
  391. package/server/nodes/tool/{task_manager.py → task_manager/__init__.py} +17 -18
  392. package/server/nodes/tool/task_manager/meta.json +3 -0
  393. package/server/nodes/tool/{write_todos.py → write_todos/__init__.py} +20 -6
  394. package/server/nodes/tool/write_todos/meta.json +3 -0
  395. package/server/nodes/trigger/{chat_trigger.py → chat_trigger/__init__.py} +11 -7
  396. package/server/nodes/trigger/chat_trigger/_events.py +53 -0
  397. package/server/nodes/trigger/chat_trigger/meta.json +3 -0
  398. package/server/nodes/trigger/{task_trigger.py → task_trigger/__init__.py} +10 -7
  399. package/server/nodes/trigger/task_trigger/meta.json +3 -0
  400. package/server/nodes/trigger/{webhook_trigger.py → webhook_trigger/__init__.py} +10 -7
  401. package/server/nodes/trigger/webhook_trigger/_events.py +54 -0
  402. package/server/nodes/trigger/webhook_trigger/meta.json +3 -0
  403. package/server/nodes/twitter/__init__.py +7 -1
  404. package/server/nodes/twitter/_base.py +86 -61
  405. package/server/nodes/twitter/_credentials.py +7 -5
  406. package/server/nodes/twitter/_events.py +101 -0
  407. package/server/nodes/twitter/_filters.py +9 -9
  408. package/server/nodes/twitter/_handlers.py +3 -1
  409. package/server/nodes/twitter/_oauth.py +1 -2
  410. package/server/nodes/twitter/_refresh.py +8 -12
  411. package/server/nodes/twitter/{twitter_receive.py → twitter_receive/__init__.py} +7 -7
  412. package/server/nodes/twitter/twitter_receive/icon.svg +1 -0
  413. package/server/nodes/twitter/twitter_receive/meta.json +3 -0
  414. package/server/nodes/twitter/{twitter_search.py → twitter_search/__init__.py} +16 -11
  415. package/server/nodes/twitter/twitter_search/icon.svg +1 -0
  416. package/server/nodes/twitter/twitter_search/meta.json +3 -0
  417. package/server/nodes/twitter/{twitter_send.py → twitter_send/__init__.py} +60 -27
  418. package/server/nodes/twitter/twitter_send/icon.svg +1 -0
  419. package/server/nodes/twitter/twitter_send/meta.json +3 -0
  420. package/server/nodes/twitter/{twitter_user.py → twitter_user/__init__.py} +34 -19
  421. package/server/nodes/twitter/twitter_user/icon.svg +1 -0
  422. package/server/nodes/twitter/twitter_user/meta.json +3 -0
  423. package/server/nodes/utility/{console.py → console/__init__.py} +17 -22
  424. package/server/nodes/utility/console/meta.json +3 -0
  425. package/server/nodes/utility/{http_request.py → http_request/__init__.py} +9 -6
  426. package/server/nodes/utility/http_request/meta.json +3 -0
  427. package/server/nodes/utility/{process_manager.py → process_manager/__init__.py} +10 -6
  428. package/server/nodes/utility/process_manager/meta.json +3 -0
  429. package/server/nodes/utility/team_monitor/meta.json +3 -0
  430. package/server/nodes/utility/{webhook_response.py → webhook_response/__init__.py} +12 -7
  431. package/server/nodes/utility/webhook_response/meta.json +3 -0
  432. package/server/nodes/visuals.json +69 -251
  433. package/server/nodes/whatsapp/__init__.py +24 -0
  434. package/server/nodes/whatsapp/_base.py +283 -338
  435. package/server/nodes/whatsapp/_credentials.py +44 -0
  436. package/server/nodes/whatsapp/_events.py +277 -0
  437. package/server/nodes/whatsapp/_filters.py +36 -37
  438. package/server/nodes/whatsapp/_handlers.py +2 -0
  439. package/server/nodes/whatsapp/_option_loaders.py +1 -3
  440. package/server/nodes/whatsapp/_refresh.py +13 -18
  441. package/server/nodes/whatsapp/_runtime.py +9 -6
  442. package/server/nodes/whatsapp/_service.py +89 -152
  443. package/server/nodes/whatsapp/meta.json +3 -0
  444. package/server/nodes/whatsapp/whatsapp_db.py +116 -54
  445. package/server/nodes/whatsapp/whatsapp_receive.py +30 -13
  446. package/server/nodes/whatsapp/whatsapp_send.py +60 -37
  447. package/server/nodes/workflow/{start.py → start/__init__.py} +1 -4
  448. package/server/nodes/workflow/start/meta.json +3 -0
  449. package/server/package-lock.json +3 -3
  450. package/server/package.json +3 -0
  451. package/server/pyproject.toml +39 -10
  452. package/server/requirements.txt +3 -5
  453. package/server/routers/__init__.py +1 -1
  454. package/server/routers/auth.py +16 -56
  455. package/server/routers/database.py +27 -50
  456. package/server/routers/nodejs_compat.py +25 -87
  457. package/server/routers/schemas.py +66 -2
  458. package/server/routers/webhook.py +12 -12
  459. package/server/routers/websocket.py +312 -1716
  460. package/server/routers/workflow.py +28 -53
  461. package/server/scripts/smoke_test_skills.py +178 -0
  462. package/server/services/__init__.py +1 -1
  463. package/server/services/_supervisor/process.py +9 -3
  464. package/server/services/_supervisor/registry.py +3 -3
  465. package/server/services/_supervisor/util.py +1 -1
  466. package/server/services/agent_team.py +15 -43
  467. package/server/services/agent_teams/__init__.py +17 -0
  468. package/server/services/agent_teams/handlers.py +195 -0
  469. package/server/services/ai.py +853 -1108
  470. package/server/services/auth.py +10 -34
  471. package/server/services/chat_client.py +5 -34
  472. package/server/services/circuit_breaker.py +2 -6
  473. package/server/services/cli_agent/__init__.py +28 -4
  474. package/server/services/cli_agent/_cli_auth.py +61 -0
  475. package/server/services/cli_agent/_handlers.py +24 -183
  476. package/server/services/cli_agent/config.py +5 -8
  477. package/server/services/cli_agent/factory.py +168 -22
  478. package/server/services/cli_agent/jsonl_watcher.py +380 -0
  479. package/server/services/cli_agent/lockfile.py +9 -2
  480. package/server/services/cli_agent/mcp_server.py +110 -34
  481. package/server/services/cli_agent/protocol.py +37 -19
  482. package/server/services/cli_agent/providers/__init__.py +8 -4
  483. package/server/services/cli_agent/providers/google_gemini.py +11 -5
  484. package/server/services/cli_agent/providers/openai_codex.py +34 -34
  485. package/server/services/cli_agent/service.py +245 -83
  486. package/server/services/cli_agent/session.py +409 -229
  487. package/server/services/cli_agent/transports/__init__.py +47 -0
  488. package/server/services/cli_agent/transports/base.py +111 -0
  489. package/server/services/cli_agent/transports/posix.py +196 -0
  490. package/server/services/cli_agent/transports/windows.py +189 -0
  491. package/server/services/cli_agent/types.py +45 -18
  492. package/server/services/cli_agent/workflow_tools.py +28 -15
  493. package/server/services/compaction.py +68 -52
  494. package/server/services/credential_registry.py +6 -20
  495. package/server/services/credentials/__init__.py +18 -0
  496. package/server/services/credentials/handlers.py +196 -0
  497. package/server/services/deployment/__init__.py +12 -1
  498. package/server/services/deployment/canary_registry.py +137 -0
  499. package/server/services/deployment/handlers.py +382 -0
  500. package/server/services/deployment/manager.py +653 -163
  501. package/server/services/deployment/poll_registry.py +2 -6
  502. package/server/services/deployment/state.py +2 -0
  503. package/server/services/deployment/triggers.py +87 -93
  504. package/server/services/event_waiter.py +47 -54
  505. package/server/services/events/__init__.py +11 -0
  506. package/server/services/events/admin_handlers.py +368 -0
  507. package/server/services/events/daemon.py +3 -1
  508. package/server/services/events/dispatch.py +188 -0
  509. package/server/services/events/envelope.py +264 -45
  510. package/server/services/events/oauth_lifecycle.py +98 -42
  511. package/server/services/events/triggers.py +3 -13
  512. package/server/services/events/verifiers/hmac_basic.py +1 -1
  513. package/server/services/events/verifiers/standard_webhooks.py +2 -4
  514. package/server/services/events/webhook.py +2 -3
  515. package/server/services/example_loader.py +73 -15
  516. package/server/services/execution/cache.py +36 -76
  517. package/server/services/execution/conditions.py +7 -20
  518. package/server/services/execution/dlq.py +20 -24
  519. package/server/services/execution/executor.py +234 -265
  520. package/server/services/execution/models.py +40 -46
  521. package/server/services/execution/recovery.py +23 -46
  522. package/server/services/handlers/__init__.py +12 -16
  523. package/server/services/handlers/todo.py +3 -6
  524. package/server/services/handlers/tools.py +143 -194
  525. package/server/services/handlers/triggers.py +24 -23
  526. package/server/services/llm/config.py +10 -1
  527. package/server/services/llm/factory.py +16 -4
  528. package/server/services/llm/messages.py +1 -5
  529. package/server/services/llm/protocol.py +9 -1
  530. package/server/services/llm/providers/anthropic.py +23 -12
  531. package/server/services/llm/providers/gemini.py +43 -22
  532. package/server/services/llm/providers/openai.py +14 -6
  533. package/server/services/llm/providers/openrouter.py +6 -1
  534. package/server/services/markdown_formatter.py +1 -2
  535. package/server/services/memory/__init__.py +2 -2
  536. package/server/services/memory/jsonl.py +6 -2
  537. package/server/services/memory/markdown.py +6 -6
  538. package/server/services/memory/state.py +6 -5
  539. package/server/services/memory_store.py +8 -12
  540. package/server/services/model_registry.py +22 -20
  541. package/server/services/node_executor.py +85 -80
  542. package/server/services/node_output_schemas.py +4 -7
  543. package/server/services/node_registry.py +40 -4
  544. package/server/services/node_spec.py +3 -7
  545. package/server/services/nodejs_client.py +4 -14
  546. package/server/services/oauth_utils.py +11 -7
  547. package/server/services/parameter_resolver.py +30 -36
  548. package/server/services/plugin/base.py +321 -38
  549. package/server/services/plugin/connection.py +12 -7
  550. package/server/services/plugin/credential.py +80 -22
  551. package/server/services/plugin/edge_walker.py +128 -105
  552. package/server/services/plugin/identifiers.py +48 -0
  553. package/server/services/plugin/interceptor.py +1 -1
  554. package/server/services/plugin/oauth.py +25 -21
  555. package/server/services/plugin/operation.py +1 -1
  556. package/server/services/plugin/polling.py +151 -26
  557. package/server/services/plugin/registry.py +52 -4
  558. package/server/services/plugin/routing.py +6 -9
  559. package/server/services/plugin/scaling.py +36 -18
  560. package/server/services/plugin/service_factories.py +95 -0
  561. package/server/services/plugin/shutdown_hooks.py +103 -0
  562. package/server/services/plugin/social_provider_registry.py +80 -0
  563. package/server/services/plugin/ws.py +2 -1
  564. package/server/services/pricing.py +26 -40
  565. package/server/services/pricing_handlers.py +90 -0
  566. package/server/services/process_service.py +33 -32
  567. package/server/services/proxy/models.py +15 -9
  568. package/server/services/proxy/service.py +26 -40
  569. package/server/services/rlm/adapters.py +43 -40
  570. package/server/services/rlm/constants.py +9 -9
  571. package/server/services/rlm/service.py +57 -45
  572. package/server/services/scheduler.py +8 -39
  573. package/server/services/settings/__init__.py +16 -0
  574. package/server/services/settings/handlers.py +275 -0
  575. package/server/services/skill_loader.py +53 -45
  576. package/server/services/skill_prompt.py +8 -6
  577. package/server/services/skills/__init__.py +23 -0
  578. package/server/services/skills/handlers.py +479 -0
  579. package/server/services/status_broadcaster.py +314 -291
  580. package/server/services/temporal/__init__.py +22 -1
  581. package/server/services/temporal/_handlers.py +65 -0
  582. package/server/services/temporal/_install.py +158 -0
  583. package/server/services/temporal/_refresh.py +57 -0
  584. package/server/services/temporal/_retry_policies.py +85 -0
  585. package/server/services/temporal/_runtime.py +181 -0
  586. package/server/services/temporal/_supervised_runtime.py +102 -0
  587. package/server/services/temporal/activities.py +168 -11
  588. package/server/services/temporal/agent_activities.py +683 -0
  589. package/server/services/temporal/agent_workflow.py +601 -0
  590. package/server/services/temporal/client.py +58 -13
  591. package/server/services/temporal/executor.py +2 -3
  592. package/server/services/temporal/plugin_activities.py +37 -2
  593. package/server/services/temporal/plugin_registry.py +82 -0
  594. package/server/services/temporal/polling_trigger_workflow.py +267 -0
  595. package/server/services/temporal/schedules.py +220 -0
  596. package/server/services/temporal/search_attributes.py +177 -0
  597. package/server/services/temporal/trigger_listener_workflow.py +378 -0
  598. package/server/services/temporal/worker.py +111 -18
  599. package/server/services/temporal/workflow.py +259 -40
  600. package/server/services/temporal/ws_client.py +22 -11
  601. package/server/services/text.py +14 -28
  602. package/server/services/tracked_http.py +29 -49
  603. package/server/services/user_auth.py +7 -21
  604. package/server/services/workflow.py +28 -20
  605. package/server/services/workflow_import.py +351 -0
  606. package/server/services/workflow_ops.py +4 -0
  607. package/server/services/workflow_storage/__init__.py +18 -0
  608. package/server/services/workflow_storage/handlers.py +132 -0
  609. package/server/services/workflow_validator.py +209 -0
  610. package/server/services/ws_handler_registry.py +80 -9
  611. package/server/skills/assistant/agent-builder-skill/SKILL.md +6 -6
  612. package/server/tests/conftest.py +54 -3
  613. package/server/tests/credentials/test_auth_service.py +9 -21
  614. package/server/tests/credentials/test_credential_broadcasts.py +116 -22
  615. package/server/tests/credentials/test_credentials_database.py +12 -38
  616. package/server/tests/credentials/test_encryption.py +3 -9
  617. package/server/tests/credentials/test_google_oauth.py +1 -3
  618. package/server/tests/credentials/test_oauth_utils.py +31 -38
  619. package/server/tests/credentials/test_twitter_oauth.py +1 -3
  620. package/server/tests/credentials/test_websocket_handlers.py +37 -72
  621. package/server/tests/fixtures/tool_names_snapshot.json +78 -0
  622. package/server/tests/llm/test_factory.py +12 -4
  623. package/server/tests/llm/test_providers.py +25 -32
  624. package/server/tests/llm/test_wiring.py +27 -22
  625. package/server/tests/nodes/_compat.py +4 -5
  626. package/server/tests/nodes/_harness.py +31 -24
  627. package/server/tests/nodes/_mocks.py +2 -6
  628. package/server/tests/nodes/test_agent_builder.py +43 -35
  629. package/server/tests/nodes/test_ai_agents.py +29 -24
  630. package/server/tests/nodes/test_ai_chat_models.py +3 -9
  631. package/server/tests/nodes/test_ai_tools.py +29 -24
  632. package/server/tests/nodes/test_android.py +34 -64
  633. package/server/tests/nodes/test_chat_utility.py +2 -2
  634. package/server/tests/nodes/test_code_fs_process.py +26 -84
  635. package/server/tests/nodes/test_document.py +23 -47
  636. package/server/tests/nodes/test_email.py +88 -51
  637. package/server/tests/nodes/test_google_workspace.py +26 -20
  638. package/server/tests/nodes/test_http_proxy.py +43 -89
  639. package/server/tests/nodes/test_search.py +3 -9
  640. package/server/tests/nodes/test_specialized_agents.py +58 -162
  641. package/server/tests/nodes/test_stripe_plugin.py +25 -5
  642. package/server/tests/nodes/test_telegram_social.py +33 -37
  643. package/server/tests/nodes/test_twitter.py +59 -150
  644. package/server/tests/nodes/test_web_automation.py +21 -51
  645. package/server/tests/nodes/test_whatsapp.py +13 -19
  646. package/server/tests/nodes/test_workflow_triggers.py +16 -45
  647. package/server/tests/services/cli_agent/test_claude_session_events.py +201 -0
  648. package/server/tests/services/cli_agent/test_jsonl_watcher.py +190 -0
  649. package/server/tests/services/cli_agent/test_mcp_server.py +67 -29
  650. package/server/tests/services/cli_agent/test_providers.py +236 -47
  651. package/server/tests/services/cli_agent/test_service.py +9 -7
  652. package/server/tests/services/memory/test_jsonl.py +30 -25
  653. package/server/tests/services/test_events.py +26 -7
  654. package/server/tests/services/test_identifiers.py +122 -0
  655. package/server/tests/services/test_process_lifecycle.py +129 -0
  656. package/server/tests/services/test_supervisor.py +0 -1
  657. package/server/tests/temporal/__init__.py +0 -0
  658. package/server/tests/temporal/test_agent_workflow.py +215 -0
  659. package/server/tests/temporal/test_dispatch.py +231 -0
  660. package/server/tests/test_admin_handlers.py +394 -0
  661. package/server/tests/test_auto_skill.py +4 -2
  662. package/server/tests/test_canary_registry.py +310 -0
  663. package/server/tests/test_chat_trigger_canary_producer.py +101 -0
  664. package/server/tests/test_cloudevents_node_parameters.py +129 -0
  665. package/server/tests/test_credential_icon.py +115 -0
  666. package/server/tests/test_cron_canary.py +511 -0
  667. package/server/tests/test_deployment_canary_listener.py +692 -0
  668. package/server/tests/test_event_framework_phase_a.py +537 -0
  669. package/server/tests/test_no_raw_prints.py +131 -0
  670. package/server/tests/test_node_spec.py +196 -103
  671. package/server/tests/test_parameter_resolver.py +20 -20
  672. package/server/tests/test_plugin_contract.py +76 -49
  673. package/server/tests/test_plugin_helpers.py +0 -1
  674. package/server/tests/test_plugin_self_containment.py +40 -47
  675. package/server/tests/test_polling_trigger_workflow.py +572 -0
  676. package/server/tests/test_retry_policies.py +146 -0
  677. package/server/tests/test_service_factories.py +168 -0
  678. package/server/tests/test_shutdown_hooks.py +199 -0
  679. package/server/tests/test_social_provider_registry.py +177 -0
  680. package/server/tests/test_status_broadcasts.py +214 -63
  681. package/server/tests/test_task_trigger_canary_producer.py +131 -0
  682. package/server/tests/test_telegram_trigger_canary_producer.py +113 -0
  683. package/server/tests/test_tool_registry.py +110 -0
  684. package/server/tests/test_trigger_listener_workflow.py +365 -0
  685. package/server/tests/test_whatsapp_trigger_canary_producer.py +164 -0
  686. package/server/tests/test_workflow_ops.py +1 -3
  687. package/server/tests/test_workflow_validator.py +791 -0
  688. package/server/uv.lock +3539 -0
  689. package/client/dist/assets/index-DQ0nwhec.js +0 -257
  690. package/client/src/assets/icons/apify/index.ts +0 -19
  691. package/client/src/assets/icons/browser/index.ts +0 -17
  692. package/client/src/assets/icons/email/index.ts +0 -22
  693. package/client/src/assets/icons/google/index.ts +0 -34
  694. package/client/src/assets/icons/llm/deepseek.svg +0 -1
  695. package/client/src/assets/icons/llm/index.ts +0 -18
  696. package/client/src/assets/icons/llm/kimi.svg +0 -1
  697. package/client/src/assets/icons/llm/mistral.svg +0 -1
  698. package/client/src/assets/icons/search/index.ts +0 -28
  699. package/client/src/assets/icons/telegram/index.ts +0 -19
  700. package/machina/buildenv.py +0 -44
  701. package/machina/cli.py +0 -55
  702. package/machina/commands/__init__.py +0 -1
  703. package/machina/commands/clean.py +0 -80
  704. package/machina/commands/daemon.py +0 -150
  705. package/machina/config.py +0 -93
  706. package/machina/platform_.py +0 -37
  707. package/machina/pyproject.toml +0 -33
  708. package/server/nodes/agent/deep_agent.py +0 -103
  709. package/server/services/agents/__init__.py +0 -9
  710. package/server/services/agents/adapters.py +0 -199
  711. package/server/services/agents/constants.py +0 -10
  712. package/server/services/agents/service.py +0 -297
  713. package/server/services/cli_agent/providers/anthropic_claude.py +0 -419
  714. /package/{machina → cli}/README.md +0 -0
  715. /package/{machina → cli}/__init__.py +0 -0
  716. /package/{client/src/assets/icons/apify → server/credentials/icons}/apify.svg +0 -0
  717. /package/{client/src/assets/icons/search/brave.svg → server/credentials/icons/brave_search.svg} +0 -0
  718. /package/{client/src/assets/icons/email/read.svg → server/credentials/icons/email_himalaya.svg} +0 -0
  719. /package/{client/src/assets/icons/search → server/credentials/icons}/perplexity.svg +0 -0
  720. /package/{client/src/assets/icons/search/google.svg → server/credentials/icons/serper.svg} +0 -0
  721. /package/{client/src/assets → server/credentials}/icons/stripe.svg +0 -0
  722. /package/{client/src/assets/icons/twitter/x.svg → server/credentials/icons/twitter.svg} +0 -0
  723. /package/{client/src/assets/icons/browser/chrome.svg → server/nodes/browser/browser/icon.svg} +0 -0
  724. /package/{client/src/assets/icons/chat/chat.svg → server/nodes/chat/chat_history/icon.svg} +0 -0
  725. /package/{client/src/assets/icons/code/javascript.svg → server/nodes/code/javascript_executor/icon.svg} +0 -0
  726. /package/{client/src/assets/icons/code/python.svg → server/nodes/code/python_executor/icon.svg} +0 -0
  727. /package/{client/src/assets/icons/code/typescript.svg → server/nodes/code/typescript_executor/icon.svg} +0 -0
  728. /package/{client/src/assets/icons/email/receive.svg → server/nodes/email/email_receive/icon.svg} +0 -0
  729. /package/{client/src/assets/icons/email/send.svg → server/nodes/email/email_send/icon.svg} +0 -0
  730. /package/{client/src/assets/icons/google/calendar.svg → server/nodes/google/calendar/icon.svg} +0 -0
  731. /package/{client/src/assets/icons/google/contacts.svg → server/nodes/google/contacts/icon.svg} +0 -0
  732. /package/{client/src/assets/icons/google/drive.svg → server/nodes/google/drive/icon.svg} +0 -0
  733. /package/{client/src/assets/icons/google/gmail.svg → server/nodes/google/gmail/icon.svg} +0 -0
  734. /package/{client/src/assets/icons/google/sheets.svg → server/nodes/google/sheets/icon.svg} +0 -0
  735. /package/{client/src/assets/icons/google/tasks.svg → server/nodes/google/tasks/icon.svg} +0 -0
  736. /package/{client/src/assets/icons/search/duckduckgo.svg → server/nodes/search/duckduckgo_search/icon.svg} +0 -0
  737. /package/{client/src/assets/icons/social/social.svg → server/nodes/social/social_receive/icon.svg} +0 -0
  738. /package/{client/src/assets/icons/telegram/telegram.svg → server/nodes/telegram/icon.svg} +0 -0
  739. /package/server/nodes/utility/{team_monitor.py → team_monitor/__init__.py} +0 -0
  740. /package/{client/src/assets/icons/whatsapp/whatsapp-db.svg → server/nodes/whatsapp/icon_whatsappDb.svg} +0 -0
  741. /package/{client/src/assets/icons/whatsapp/whatsapp-receive.svg → server/nodes/whatsapp/icon_whatsappReceive.svg} +0 -0
  742. /package/{client/src/assets/icons/whatsapp/whatsapp-send.svg → server/nodes/whatsapp/icon_whatsappSend.svg} +0 -0
  743. /package/{client/src/assets/icons → server/nodes}/whatsapp/whatsapp.svg +0 -0
@@ -44,53 +44,22 @@ class StatusBroadcaster:
44
44
  "connected_devices": [],
45
45
  "connection_type": None,
46
46
  "qr_data": None,
47
- "session_token": None
48
- },
49
- "whatsapp": {
50
- "connected": False,
51
- "has_session": False,
52
- "running": False,
53
- "pairing": False,
54
- "device_id": None,
55
- "qr": None
56
- },
57
- "twitter": {
58
- "connected": False,
59
- "username": None,
60
- "user_id": None,
61
- "name": None,
62
- "profile_image_url": None
47
+ "session_token": None,
63
48
  },
49
+ "whatsapp": {"connected": False, "has_session": False, "running": False, "pairing": False, "device_id": None, "qr": None},
50
+ "twitter": {"connected": False, "username": None, "user_id": None, "name": None, "profile_image_url": None},
64
51
  "google": {
65
52
  "connected": False,
66
53
  "email": None,
67
54
  "name": None,
68
55
  },
69
- "telegram": {
70
- "connected": False,
71
- "bot_id": None,
72
- "bot_username": None,
73
- "bot_name": None
74
- },
56
+ "telegram": {"connected": False, "bot_id": None, "bot_username": None, "bot_name": None},
75
57
  "api_keys": {}, # provider -> validation status
76
58
  "nodes": {}, # node_id -> node status
77
59
  "variables": {}, # variable_name -> value
78
- "workflow": {
79
- "executing": False,
80
- "current_node": None
81
- },
82
- "workflow_lock": {
83
- "locked": False,
84
- "workflow_id": None,
85
- "locked_at": None,
86
- "reason": None
87
- },
88
- "deployment": {
89
- "isRunning": False,
90
- "activeRuns": 0,
91
- "status": "idle",
92
- "workflow_id": None
93
- }
60
+ "workflow": {"executing": False, "current_node": None},
61
+ "workflow_lock": {"locked": False, "workflow_id": None, "locked_at": None, "reason": None},
62
+ "deployment": {"isRunning": False, "activeRuns": 0, "status": "idle", "workflow_id": None},
94
63
  }
95
64
 
96
65
  async def connect(self, websocket: WebSocket):
@@ -113,10 +82,7 @@ class StatusBroadcaster:
113
82
  logger.info(f"[StatusBroadcaster] Client connected. Total: {len(self._connections)}")
114
83
 
115
84
  try:
116
- await websocket.send_json({
117
- "type": "initial_status",
118
- "data": self._status
119
- })
85
+ await websocket.send_json({"type": "initial_status", "data": self._status})
120
86
  except Exception as e:
121
87
  logger.error(f"[StatusBroadcaster] Failed to send initial status: {e}")
122
88
 
@@ -155,10 +121,12 @@ class StatusBroadcaster:
155
121
  return
156
122
 
157
123
  event = WorkflowEvent.deployment_snapshot(running_ids)
158
- await websocket.send_json({
159
- "type": "deployment_snapshot",
160
- "data": event.model_dump(mode="json"),
161
- })
124
+ await websocket.send_json(
125
+ {
126
+ "type": "deployment_snapshot",
127
+ "data": event.model_dump(mode="json"),
128
+ }
129
+ )
162
130
 
163
131
  async def disconnect(self, websocket: WebSocket):
164
132
  """Remove a WebSocket connection."""
@@ -244,12 +212,7 @@ class StatusBroadcaster:
244
212
  # =========================================================================
245
213
 
246
214
  async def update_api_key_status(
247
- self,
248
- provider: str,
249
- valid: bool,
250
- message: Optional[str] = None,
251
- has_key: bool = True,
252
- models: Optional[List[str]] = None
215
+ self, provider: str, valid: bool, message: Optional[str] = None, has_key: bool = True, models: Optional[List[str]] = None
253
216
  ):
254
217
  """Update API key validation status and broadcast."""
255
218
  self._status["api_keys"][provider] = {
@@ -257,14 +220,10 @@ class StatusBroadcaster:
257
220
  "hasKey": has_key,
258
221
  "message": message,
259
222
  "models": models or [],
260
- "timestamp": asyncio.get_event_loop().time()
223
+ "timestamp": asyncio.get_event_loop().time(),
261
224
  }
262
225
 
263
- await self.broadcast({
264
- "type": "api_key_status",
265
- "provider": provider,
266
- "data": self._status["api_keys"][provider]
267
- })
226
+ await self.broadcast({"type": "api_key_status", "provider": provider, "data": self._status["api_keys"][provider]})
268
227
 
269
228
  def get_api_key_status(self, provider: str) -> Optional[Dict[str, Any]]:
270
229
  """Get API key validation status for a provider."""
@@ -289,15 +248,23 @@ class StatusBroadcaster:
289
248
  *,
290
249
  provider: str,
291
250
  customer_id: Optional[str] = None,
251
+ workflow_id: Optional[str] = None,
252
+ **data_extra: Any,
292
253
  ) -> None:
293
- """Emit a CloudEvents-typed credential-mutation broadcast.
254
+ """Emit a CloudEvents-typed credential broadcast.
294
255
 
295
256
  Args:
296
257
  event_type: CloudEvents `type` field. Convention:
297
- ``"credential.api_key.saved"`` / ``".deleted"``,
298
- ``"credential.oauth.disconnected"``.
258
+ ``"credential.api_key.saved"`` / ``".deleted"`` /
259
+ ``".runtime_failed"``, ``"credential.oauth.connected"`` /
260
+ ``".disconnected"`` / ``".runtime_failed"``.
299
261
  provider: Provider id (e.g. ``"openai"``, ``"twitter"``).
300
262
  customer_id: For multi-tenant OAuth flows. Default omitted.
263
+ workflow_id: Optional CloudEvents extension attribute scoping
264
+ runtime events to the workflow that triggered them.
265
+ **data_extra: Additional fields merged into the envelope's
266
+ ``data`` block (e.g. ``reason``, ``node_id``, ``error``
267
+ for runtime failure events).
301
268
  """
302
269
  # Local import keeps the broadcaster module independent of the
303
270
  # event framework's load order during startup.
@@ -307,16 +274,92 @@ class StatusBroadcaster:
307
274
  source="machinaos://services/credentials",
308
275
  type=event_type,
309
276
  subject=provider,
277
+ workflow_id=workflow_id,
310
278
  data={
311
279
  "provider": provider,
312
280
  **({"customer_id": customer_id} if customer_id else {}),
281
+ **data_extra,
313
282
  },
314
283
  )
315
284
 
316
- await self.broadcast({
317
- "type": "credential_catalogue_updated",
318
- "data": event.model_dump(mode="json"),
319
- })
285
+ await self.broadcast(
286
+ {
287
+ "type": "credential_catalogue_updated",
288
+ "data": event.model_dump(mode="json"),
289
+ }
290
+ )
291
+
292
+ async def broadcast_workflow_lifecycle(
293
+ self,
294
+ stage: str,
295
+ *,
296
+ workflow_id: str,
297
+ **data_extra: Any,
298
+ ) -> None:
299
+ """Emit a CloudEvents-typed workflow.{stage} broadcast.
300
+
301
+ Wraps :meth:`WorkflowEvent.workflow_lifecycle` so callers don't
302
+ hand-build envelopes. ``stage`` matches the factory's Literal
303
+ (``imported`` / ``deployment.started`` / etc.). Wire-format key
304
+ is ``workflow_lifecycle`` — the frontend's ``WebSocketContext``
305
+ routes on this key and invalidates the workflows query so the
306
+ sidebar refreshes across all connected clients.
307
+ """
308
+ from services.events import WorkflowEvent
309
+
310
+ event = WorkflowEvent.workflow_lifecycle(
311
+ stage=stage, # type: ignore[arg-type]
312
+ workflow_id=workflow_id,
313
+ data=data_extra or None,
314
+ )
315
+ await self.broadcast(
316
+ {
317
+ "type": "workflow_lifecycle",
318
+ "data": event.model_dump(mode="json"),
319
+ }
320
+ )
321
+
322
+ async def broadcast_node_parameters_updated(
323
+ self,
324
+ node_id: str,
325
+ *,
326
+ parameters: Dict[str, Any],
327
+ workflow_id: Optional[str] = None,
328
+ version: int = 1,
329
+ source_hint: str = "user",
330
+ ) -> None:
331
+ """Emit a CloudEvents-typed ``node.parameters.updated`` event.
332
+
333
+ Replaces three legacy raw-dict broadcast sites with one typed
334
+ envelope (RFC §6.4 CloudEvents discipline). Wire-format key
335
+ ``node_parameters_updated`` stays the same for FE back-compat;
336
+ only the inner payload becomes the typed envelope.
337
+
338
+ Callers:
339
+ - ``routers/websocket.py:handle_save_node_parameters`` (user
340
+ edited the parameter panel — ``source_hint="user"``).
341
+ - ``services/cli_agent/service.py:_persist_memory`` (Claude
342
+ Code CLI memory bridge appended a turn —
343
+ ``source_hint="cli"``).
344
+ - ``services/temporal/agent_activities.py:persist_agent_turn``
345
+ (F4.B AgentWorkflow per-turn memory append —
346
+ ``source_hint="agent"``).
347
+ """
348
+ from services.events import WorkflowEvent
349
+
350
+ event = WorkflowEvent.node_parameters_updated(
351
+ node_id,
352
+ parameters=parameters,
353
+ workflow_id=workflow_id,
354
+ version=version,
355
+ source_hint=source_hint,
356
+ )
357
+ await self.broadcast(
358
+ {
359
+ "type": "node_parameters_updated",
360
+ "data": event.model_dump(mode="json"),
361
+ }
362
+ )
320
363
 
321
364
  async def broadcast_agent_progress(
322
365
  self,
@@ -346,42 +389,146 @@ class StatusBroadcaster:
346
389
  phase=phase,
347
390
  )
348
391
 
349
- await self.broadcast({
350
- "type": "agent_progress",
351
- "data": event.model_dump(mode="json"),
352
- })
392
+ await self.broadcast(
393
+ {
394
+ "type": "agent_progress",
395
+ "data": event.model_dump(mode="json"),
396
+ }
397
+ )
398
+
399
+ async def broadcast_claude_session_spawned(
400
+ self,
401
+ memory_node_id: str,
402
+ *,
403
+ session_uuid: str,
404
+ pid: int,
405
+ workflow_id: Optional[str] = None,
406
+ ) -> None:
407
+ """Emit ``claude.session.spawned`` when a pooled claude is
408
+ cold-started. Single wire key for all four session-lifecycle
409
+ events; FE discriminates on envelope.type.
410
+ """
411
+ from services.events import WorkflowEvent
412
+
413
+ event = WorkflowEvent.claude_session_spawned(
414
+ memory_node_id,
415
+ session_uuid=session_uuid,
416
+ pid=pid,
417
+ workflow_id=workflow_id,
418
+ )
419
+ await self.broadcast(
420
+ {
421
+ "type": "claude_session_event",
422
+ "data": event.model_dump(mode="json"),
423
+ }
424
+ )
425
+
426
+ async def broadcast_claude_session_cleared(
427
+ self,
428
+ memory_node_id: str,
429
+ *,
430
+ old_session_uuid: str,
431
+ new_session_uuid: str,
432
+ workflow_id: Optional[str] = None,
433
+ ) -> None:
434
+ """Emit ``claude.session.cleared`` after ``/clear`` minted a new
435
+ session UUID. Carries both old + new so the FE can update
436
+ any session-uuid display + warn if there are open references."""
437
+ from services.events import WorkflowEvent
438
+
439
+ event = WorkflowEvent.claude_session_cleared(
440
+ memory_node_id,
441
+ old_session_uuid=old_session_uuid,
442
+ new_session_uuid=new_session_uuid,
443
+ workflow_id=workflow_id,
444
+ )
445
+ await self.broadcast(
446
+ {
447
+ "type": "claude_session_event",
448
+ "data": event.model_dump(mode="json"),
449
+ }
450
+ )
451
+
452
+ async def broadcast_claude_session_terminated(
453
+ self,
454
+ memory_node_id: str,
455
+ *,
456
+ reason: str,
457
+ session_uuid: Optional[str] = None,
458
+ workflow_id: Optional[str] = None,
459
+ ) -> None:
460
+ """Emit ``claude.session.terminated`` when a pooled session
461
+ ends. ``reason`` is one of ``idle / crashed / evicted /
462
+ shutdown / explicit`` (typed enum in
463
+ :class:`WorkflowEvent.claude_session_terminated`)."""
464
+ from services.events import WorkflowEvent
465
+
466
+ event = WorkflowEvent.claude_session_terminated(
467
+ memory_node_id,
468
+ reason=reason, # type: ignore[arg-type]
469
+ session_uuid=session_uuid,
470
+ workflow_id=workflow_id,
471
+ )
472
+ await self.broadcast(
473
+ {
474
+ "type": "claude_session_event",
475
+ "data": event.model_dump(mode="json"),
476
+ }
477
+ )
478
+
479
+ async def broadcast_claude_session_usage(
480
+ self,
481
+ memory_node_id: str,
482
+ *,
483
+ session_uuid: str,
484
+ total_cost_usd: Optional[float] = None,
485
+ input_tokens: int = 0,
486
+ output_tokens: int = 0,
487
+ cache_read_input_tokens: int = 0,
488
+ cache_creation_input_tokens: int = 0,
489
+ duration_ms: Optional[int] = None,
490
+ num_turns: Optional[int] = None,
491
+ workflow_id: Optional[str] = None,
492
+ ) -> None:
493
+ """Emit ``claude.session.usage`` after each turn's ``result``
494
+ event. Replaces the (unparseable) ``/usage`` TUI scrape with
495
+ structured data straight from the JSONL ``result.usage`` block.
496
+ FE renders a usage panel on simpleMemory by subscribing here."""
497
+ from services.events import WorkflowEvent
498
+
499
+ event = WorkflowEvent.claude_session_usage(
500
+ memory_node_id,
501
+ session_uuid=session_uuid,
502
+ total_cost_usd=total_cost_usd,
503
+ input_tokens=input_tokens,
504
+ output_tokens=output_tokens,
505
+ cache_read_input_tokens=cache_read_input_tokens,
506
+ cache_creation_input_tokens=cache_creation_input_tokens,
507
+ duration_ms=duration_ms,
508
+ num_turns=num_turns,
509
+ workflow_id=workflow_id,
510
+ )
511
+ await self.broadcast(
512
+ {
513
+ "type": "claude_session_usage",
514
+ "data": event.model_dump(mode="json"),
515
+ }
516
+ )
353
517
 
354
518
  # =========================================================================
355
519
  # Android Status Updates
356
520
  # =========================================================================
357
521
 
358
- async def update_android_status(
359
- self,
360
- connected: bool,
361
- paired: bool = False,
362
- device_id: Optional[str] = None,
363
- device_name: Optional[str] = None,
364
- connected_devices: Optional[List[str]] = None,
365
- connection_type: Optional[str] = None,
366
- qr_data: Optional[str] = None,
367
- session_token: Optional[str] = None
368
- ):
369
- """Update Android relay connection status and broadcast."""
370
- self._status["android"] = {
371
- "connected": connected,
372
- "paired": paired,
373
- "device_id": device_id,
374
- "device_name": device_name,
375
- "connected_devices": connected_devices or [],
376
- "connection_type": connection_type,
377
- "qr_data": qr_data,
378
- "session_token": session_token
379
- }
380
-
381
- await self.broadcast({
382
- "type": "android_status",
383
- "data": self._status["android"]
384
- })
522
+ # Wave 12 B1-B3: all three ``update_<plugin>_status`` methods MOVED
523
+ # to per-plugin ``_events.py:broadcast_<plugin>_status`` wrappers
524
+ # (android / whatsapp / telegram). Each plugin owns its broadcast
525
+ # shape + the dual-emit (legacy raw + typed CloudEvents sibling).
526
+ # The shared ``_emit_connection_typed`` helper retired with B3
527
+ # because no remaining caller uses it — plugin-specific typed
528
+ # factories (e.g. ``android_connection_status``) replace the
529
+ # cross-plugin parametrised helper per RFC §6.4. Status cache
530
+ # slots in ``self._status[<plugin>]`` stay so ``get_status()`` +
531
+ # WS-connect initial snapshot keep working.
385
532
 
386
533
  # =========================================================================
387
534
  # Status updates -- per-service refresh bodies live in their plugin
@@ -392,59 +539,18 @@ class StatusBroadcaster:
392
539
  # zero per-plugin knowledge.
393
540
  # =========================================================================
394
541
 
395
- async def update_whatsapp_status(
396
- self,
397
- connected: bool,
398
- has_session: bool = False,
399
- running: bool = False,
400
- pairing: bool = False,
401
- device_id: Optional[str] = None,
402
- qr: Optional[str] = None
403
- ):
404
- """Update WhatsApp connection status and broadcast."""
405
- import time
406
- self._status["whatsapp"] = {
407
- "connected": connected,
408
- "has_session": has_session,
409
- "running": running,
410
- "pairing": pairing,
411
- "device_id": device_id,
412
- "qr": qr,
413
- "timestamp": time.time()
414
- }
415
-
416
- await self.broadcast({
417
- "type": "whatsapp_status",
418
- "data": self._status["whatsapp"]
419
- })
542
+ # Wave 12 B2: ``update_whatsapp_status`` MOVED to
543
+ # ``nodes/whatsapp/_events.py:broadcast_whatsapp_status``. Plus the
544
+ # 7 send_custom_event callsites (message_sent/received, 4 newsletter
545
+ # events, history_sync_complete) all moved to the plugin's
546
+ # ``_events.py`` typed wrappers. Status cache slot stays here.
420
547
 
421
548
  # =========================================================================
422
549
  # Telegram Status Updates
423
550
  # =========================================================================
424
551
 
425
- async def update_telegram_status(
426
- self,
427
- connected: bool,
428
- bot_id: Optional[int] = None,
429
- bot_username: Optional[str] = None,
430
- bot_name: Optional[str] = None,
431
- owner_chat_id: Optional[int] = None
432
- ):
433
- """Update Telegram bot connection status and broadcast."""
434
- import time
435
- self._status["telegram"] = {
436
- "connected": connected,
437
- "bot_id": bot_id,
438
- "bot_username": bot_username,
439
- "bot_name": bot_name,
440
- "owner_chat_id": owner_chat_id,
441
- "timestamp": time.time()
442
- }
443
-
444
- await self.broadcast({
445
- "type": "telegram_status",
446
- "data": self._status["telegram"]
447
- })
552
+ # Wave 12 B3: ``update_telegram_status`` MOVED to
553
+ # ``nodes/telegram/_events.py:broadcast_telegram_status``.
448
554
 
449
555
  # =========================================================================
450
556
  # Node Status Updates
@@ -455,7 +561,7 @@ class StatusBroadcaster:
455
561
  node_id: str,
456
562
  status: str, # "idle", "executing", "waiting", "success", "error"
457
563
  data: Optional[Dict[str, Any]] = None,
458
- workflow_id: Optional[str] = None
564
+ workflow_id: Optional[str] = None,
459
565
  ):
460
566
  """Update a specific node's status and broadcast.
461
567
 
@@ -465,27 +571,21 @@ class StatusBroadcaster:
465
571
  data: Optional status data
466
572
  workflow_id: Optional workflow ID to scope the status update (n8n pattern)
467
573
  """
468
- logger.debug(f"[BROADCAST] update_node_status: node={node_id}, status={status}, workflow={workflow_id}, connections={len(self._connections)}")
574
+ logger.debug(
575
+ f"[BROADCAST] update_node_status: node={node_id}, status={status}, workflow={workflow_id}, connections={len(self._connections)}"
576
+ )
469
577
  self._status["nodes"][node_id] = {
470
578
  "status": status,
471
579
  "data": data or {},
472
580
  "timestamp": asyncio.get_event_loop().time(),
473
- "workflow_id": workflow_id
581
+ "workflow_id": workflow_id,
474
582
  }
475
583
 
476
- await self.broadcast({
477
- "type": "node_status",
478
- "node_id": node_id,
479
- "workflow_id": workflow_id,
480
- "data": self._status["nodes"][node_id]
481
- })
584
+ await self.broadcast(
585
+ {"type": "node_status", "node_id": node_id, "workflow_id": workflow_id, "data": self._status["nodes"][node_id]}
586
+ )
482
587
 
483
- async def update_node_output(
484
- self,
485
- node_id: str,
486
- output: Any,
487
- workflow_id: Optional[str] = None
488
- ):
588
+ async def update_node_output(self, node_id: str, output: Any, workflow_id: Optional[str] = None):
489
589
  """Update a node's output data and broadcast."""
490
590
  if node_id not in self._status["nodes"]:
491
591
  self._status["nodes"][node_id] = {"status": "idle", "data": {}}
@@ -494,12 +594,7 @@ class StatusBroadcaster:
494
594
  if workflow_id:
495
595
  self._status["nodes"][node_id]["workflow_id"] = workflow_id
496
596
 
497
- await self.broadcast({
498
- "type": "node_output",
499
- "node_id": node_id,
500
- "workflow_id": workflow_id,
501
- "output": output
502
- })
597
+ await self.broadcast({"type": "node_output", "node_id": node_id, "workflow_id": workflow_id, "output": output})
503
598
 
504
599
  # =========================================================================
505
600
  # Variable Updates
@@ -509,20 +604,13 @@ class StatusBroadcaster:
509
604
  """Update a workflow variable and broadcast."""
510
605
  self._status["variables"][name] = value
511
606
 
512
- await self.broadcast({
513
- "type": "variable_update",
514
- "name": name,
515
- "value": value
516
- })
607
+ await self.broadcast({"type": "variable_update", "name": name, "value": value})
517
608
 
518
609
  async def update_variables(self, variables: Dict[str, Any]):
519
610
  """Update multiple variables at once and broadcast."""
520
611
  self._status["variables"].update(variables)
521
612
 
522
- await self.broadcast({
523
- "type": "variables_update",
524
- "variables": variables
525
- })
613
+ await self.broadcast({"type": "variables_update", "variables": variables})
526
614
 
527
615
  # =========================================================================
528
616
  # Workflow Status Updates
@@ -561,11 +649,13 @@ class StatusBroadcaster:
561
649
  "progress": progress,
562
650
  }
563
651
 
564
- await self.broadcast({
565
- "type": "workflow_status",
566
- "workflow_id": workflow_id,
567
- "data": payload,
568
- })
652
+ await self.broadcast(
653
+ {
654
+ "type": "workflow_status",
655
+ "workflow_id": workflow_id,
656
+ "data": payload,
657
+ }
658
+ )
569
659
 
570
660
  async def workflow_run_started(self, workflow_id: Optional[str]) -> bool:
571
661
  """Mark a new active run for `workflow_id`.
@@ -583,7 +673,8 @@ class StatusBroadcaster:
583
673
  went_active = prev == 0
584
674
  if went_active:
585
675
  await self.update_workflow_status(
586
- executing=True, workflow_id=workflow_id,
676
+ executing=True,
677
+ workflow_id=workflow_id,
587
678
  )
588
679
  return went_active
589
680
 
@@ -620,7 +711,8 @@ class StatusBroadcaster:
620
711
  went_idle = prev > 0 and new_count == 0
621
712
  if went_idle:
622
713
  await self.update_workflow_status(
623
- executing=False, workflow_id=workflow_id,
714
+ executing=False,
715
+ workflow_id=workflow_id,
624
716
  )
625
717
  if clear_stuck_nodes:
626
718
  # include_waiting=False (default) -- don't touch deployment
@@ -660,10 +752,9 @@ class StatusBroadcaster:
660
752
 
661
753
  statuses = ("executing", "waiting") if include_waiting else ("executing",)
662
754
  stuck = [
663
- (nid, info) for nid, info in self._status["nodes"].items()
664
- if info.get("workflow_id") == workflow_id
665
- and info.get("status") in statuses
666
- and not is_node_in_active_delegation(nid)
755
+ (nid, info)
756
+ for nid, info in self._status["nodes"].items()
757
+ if info.get("workflow_id") == workflow_id and info.get("status") in statuses and not is_node_in_active_delegation(nid)
667
758
  ]
668
759
  for node_id, _info in stuck:
669
760
  try:
@@ -671,7 +762,8 @@ class StatusBroadcaster:
671
762
  except Exception as e:
672
763
  logger.warning(
673
764
  "[StatusBroadcaster] Failed to clear stuck node %s: %s",
674
- node_id, e,
765
+ node_id,
766
+ e,
675
767
  )
676
768
  return len(stuck)
677
769
 
@@ -694,7 +786,7 @@ class StatusBroadcaster:
694
786
  active_runs: int = 0,
695
787
  workflow_id: Optional[str] = None,
696
788
  data: Optional[Dict[str, Any]] = None,
697
- error: Optional[str] = None
789
+ error: Optional[str] = None,
698
790
  ):
699
791
  """Update deployment status and broadcast.
700
792
 
@@ -709,31 +801,16 @@ class StatusBroadcaster:
709
801
  data: Optional additional data (e.g., run_id, trigger info)
710
802
  error: Optional error message if status is 'error'
711
803
  """
712
- self._status["deployment"] = {
713
- "isRunning": is_running,
714
- "activeRuns": active_runs,
715
- "status": status,
716
- "workflow_id": workflow_id
717
- }
804
+ self._status["deployment"] = {"isRunning": is_running, "activeRuns": active_runs, "status": status, "workflow_id": workflow_id}
718
805
 
719
806
  # Broadcast deployment_status message (matches frontend handler)
720
- await self.broadcast({
721
- "type": "deployment_status",
722
- "status": status,
723
- "workflow_id": workflow_id,
724
- "data": data,
725
- "error": error
726
- })
807
+ await self.broadcast({"type": "deployment_status", "status": status, "workflow_id": workflow_id, "data": data, "error": error})
727
808
 
728
809
  # =========================================================================
729
810
  # Workflow Lock Management (Per-Workflow Locks - n8n pattern)
730
811
  # =========================================================================
731
812
 
732
- async def lock_workflow(
733
- self,
734
- workflow_id: str,
735
- reason: str = "deployment"
736
- ) -> bool:
813
+ async def lock_workflow(self, workflow_id: str, reason: str = "deployment") -> bool:
737
814
  """Lock a specific workflow to prevent concurrent modifications.
738
815
 
739
816
  Per-workflow locking (n8n pattern): Each workflow has its own independent lock.
@@ -756,29 +833,17 @@ class StatusBroadcaster:
756
833
  if workflow_id in self._status["workflow_locks"]:
757
834
  existing_lock = self._status["workflow_locks"][workflow_id]
758
835
  if existing_lock.get("locked"):
759
- logger.warning(
760
- f"[WorkflowLock] Workflow {workflow_id} is already locked "
761
- f"for {existing_lock.get('reason')}"
762
- )
836
+ logger.warning(f"[WorkflowLock] Workflow {workflow_id} is already locked " f"for {existing_lock.get('reason')}")
763
837
  return False
764
838
 
765
839
  # Lock this specific workflow
766
- lock_info = {
767
- "locked": True,
768
- "workflow_id": workflow_id,
769
- "locked_at": time.time(),
770
- "reason": reason
771
- }
840
+ lock_info = {"locked": True, "workflow_id": workflow_id, "locked_at": time.time(), "reason": reason}
772
841
  self._status["workflow_locks"][workflow_id] = lock_info
773
842
 
774
843
  # Also update legacy single lock for backward compatibility
775
844
  self._status["workflow_lock"] = lock_info.copy()
776
845
 
777
- await self.broadcast({
778
- "type": "workflow_lock",
779
- "workflow_id": workflow_id,
780
- "data": lock_info
781
- })
846
+ await self.broadcast({"type": "workflow_lock", "workflow_id": workflow_id, "data": lock_info})
782
847
 
783
848
  logger.info(f"[WorkflowLock] Locked workflow {workflow_id} for {reason}")
784
849
  return True
@@ -811,23 +876,15 @@ class StatusBroadcaster:
811
876
 
812
877
  # Update legacy single lock if it was for this workflow
813
878
  if self._status["workflow_lock"].get("workflow_id") == workflow_id:
814
- self._status["workflow_lock"] = {
815
- "locked": False,
816
- "workflow_id": None,
817
- "locked_at": None,
818
- "reason": None
819
- }
879
+ self._status["workflow_lock"] = {"locked": False, "workflow_id": None, "locked_at": None, "reason": None}
820
880
 
821
- await self.broadcast({
822
- "type": "workflow_lock",
823
- "workflow_id": workflow_id,
824
- "data": {
825
- "locked": False,
881
+ await self.broadcast(
882
+ {
883
+ "type": "workflow_lock",
826
884
  "workflow_id": workflow_id,
827
- "locked_at": None,
828
- "reason": None
885
+ "data": {"locked": False, "workflow_id": workflow_id, "locked_at": None, "reason": None},
829
886
  }
830
- })
887
+ )
831
888
 
832
889
  logger.info(f"[WorkflowLock] Unlocked workflow {workflow_id}")
833
890
  return True
@@ -847,10 +904,7 @@ class StatusBroadcaster:
847
904
 
848
905
  if workflow_id is None:
849
906
  # Check if ANY workflow is locked
850
- return any(
851
- lock.get("locked", False)
852
- for lock in self._status["workflow_locks"].values()
853
- )
907
+ return any(lock.get("locked", False) for lock in self._status["workflow_locks"].values())
854
908
 
855
909
  # Check specific workflow
856
910
  lock = self._status["workflow_locks"].get(workflow_id, {})
@@ -875,7 +929,7 @@ class StatusBroadcaster:
875
929
  "locked": lock.get("locked", False),
876
930
  "workflow_id": workflow_id,
877
931
  "locked_at": lock.get("locked_at"),
878
- "reason": lock.get("reason")
932
+ "reason": lock.get("reason"),
879
933
  }
880
934
 
881
935
  # Return legacy single lock for backward compatibility
@@ -885,11 +939,7 @@ class StatusBroadcaster:
885
939
  """Get all active workflow locks."""
886
940
  if "workflow_locks" not in self._status:
887
941
  return {}
888
- return {
889
- wid: lock.copy()
890
- for wid, lock in self._status["workflow_locks"].items()
891
- if lock.get("locked")
892
- }
942
+ return {wid: lock.copy() for wid, lock in self._status["workflow_locks"].items() if lock.get("locked")}
893
943
 
894
944
  # =========================================================================
895
945
  # Console Log Updates
@@ -922,16 +972,14 @@ class StatusBroadcaster:
922
972
  # Save to database for persistence
923
973
  try:
924
974
  from core.container import container
975
+
925
976
  database = container.database()
926
977
  await database.add_console_log(log_data)
927
978
  except Exception as e:
928
979
  logger.warning(f"[StatusBroadcaster] Failed to persist console log: {e}")
929
980
 
930
981
  # Broadcast to all clients
931
- await self.broadcast({
932
- "type": "console_log",
933
- "data": log_data
934
- })
982
+ await self.broadcast({"type": "console_log", "data": log_data})
935
983
 
936
984
  logger.debug(f"[StatusBroadcaster] Console log broadcast: label={log_data.get('label')}")
937
985
 
@@ -941,10 +989,7 @@ class StatusBroadcaster:
941
989
  return []
942
990
 
943
991
  if workflow_id:
944
- return [
945
- log for log in self._status["console_logs"]
946
- if log.get("workflow_id") == workflow_id
947
- ]
992
+ return [log for log in self._status["console_logs"] if log.get("workflow_id") == workflow_id]
948
993
  return list(self._status["console_logs"])
949
994
 
950
995
  async def clear_console_logs(self, workflow_id: Optional[str] = None):
@@ -954,17 +999,11 @@ class StatusBroadcaster:
954
999
  return
955
1000
 
956
1001
  if workflow_id:
957
- self._status["console_logs"] = [
958
- log for log in self._status["console_logs"]
959
- if log.get("workflow_id") != workflow_id
960
- ]
1002
+ self._status["console_logs"] = [log for log in self._status["console_logs"] if log.get("workflow_id") != workflow_id]
961
1003
  else:
962
1004
  self._status["console_logs"] = []
963
1005
 
964
- await self.broadcast({
965
- "type": "console_logs_cleared",
966
- "workflow_id": workflow_id
967
- })
1006
+ await self.broadcast({"type": "console_logs_cleared", "workflow_id": workflow_id})
968
1007
 
969
1008
  # =========================================================================
970
1009
  # Terminal Log Updates
@@ -993,10 +1032,7 @@ class StatusBroadcaster:
993
1032
  self._status["terminal_logs"] = self._status["terminal_logs"][-200:]
994
1033
 
995
1034
  # Broadcast to all clients
996
- await self.broadcast({
997
- "type": "terminal_log",
998
- "data": log_data
999
- })
1035
+ await self.broadcast({"type": "terminal_log", "data": log_data})
1000
1036
 
1001
1037
  def get_terminal_logs(self) -> List[Dict[str, Any]]:
1002
1038
  """Get terminal log history."""
@@ -1007,9 +1043,7 @@ class StatusBroadcaster:
1007
1043
  async def clear_terminal_logs(self):
1008
1044
  """Clear terminal log history."""
1009
1045
  self._status["terminal_logs"] = []
1010
- await self.broadcast({
1011
- "type": "terminal_logs_cleared"
1012
- })
1046
+ await self.broadcast({"type": "terminal_logs_cleared"})
1013
1047
 
1014
1048
  # =========================================================================
1015
1049
  # Agent Team Updates
@@ -1023,12 +1057,7 @@ class StatusBroadcaster:
1023
1057
  event_type: Event type (team_created, task_added, task_claimed, etc.)
1024
1058
  data: Event data
1025
1059
  """
1026
- await self.broadcast({
1027
- "type": "team_event",
1028
- "team_id": team_id,
1029
- "event_type": event_type,
1030
- "data": data
1031
- })
1060
+ await self.broadcast({"type": "team_event", "team_id": team_id, "event_type": event_type, "data": data})
1032
1061
 
1033
1062
  # =========================================================================
1034
1063
  # Generic Updates
@@ -1042,15 +1071,13 @@ class StatusBroadcaster:
1042
1071
  See DESIGN.md section "Cross-Thread Event Dispatch" for pattern details.
1043
1072
  """
1044
1073
  # Broadcast to all WebSocket clients
1045
- await self.broadcast({
1046
- "type": event_type,
1047
- "data": data
1048
- })
1074
+ await self.broadcast({"type": event_type, "data": data})
1049
1075
 
1050
1076
  # Dispatch to event waiters (for trigger nodes)
1051
1077
  # Use dispatch_async directly - we're in async context
1052
1078
  try:
1053
1079
  from services import event_waiter
1080
+
1054
1081
  event_data = data if isinstance(data, dict) else {"data": data}
1055
1082
  resolved_count = await event_waiter.dispatch_async(event_type, event_data)
1056
1083
  if resolved_count > 0:
@@ -1086,9 +1113,7 @@ class StatusBroadcaster:
1086
1113
  clearing the visible state.
1087
1114
  """
1088
1115
  had_status = node_id in self._status["nodes"]
1089
- previous_workflow = (
1090
- self._status["nodes"][node_id].get("workflow_id") if had_status else None
1091
- )
1116
+ previous_workflow = self._status["nodes"][node_id].get("workflow_id") if had_status else None
1092
1117
  self._status["nodes"][node_id] = {
1093
1118
  "status": "idle",
1094
1119
  "data": {},
@@ -1097,11 +1122,13 @@ class StatusBroadcaster:
1097
1122
  "cleared": True,
1098
1123
  }
1099
1124
  logger.info(f"[StatusBroadcaster] Reset node status to idle: {node_id}")
1100
- await self.broadcast({
1101
- "type": "node_status_cleared",
1102
- "node_id": node_id,
1103
- "workflow_id": previous_workflow,
1104
- })
1125
+ await self.broadcast(
1126
+ {
1127
+ "type": "node_status_cleared",
1128
+ "node_id": node_id,
1129
+ "workflow_id": previous_workflow,
1130
+ }
1131
+ )
1105
1132
  return had_status
1106
1133
 
1107
1134
  def get_variable(self, name: str) -> Any:
@@ -1125,9 +1152,7 @@ class StatusBroadcaster:
1125
1152
 
1126
1153
  import typing as _typing # local alias to avoid shadowing module-level name
1127
1154
 
1128
- _ServiceRefreshCallback = _typing.Callable[
1129
- ["StatusBroadcaster"], _typing.Awaitable[None]
1130
- ]
1155
+ _ServiceRefreshCallback = _typing.Callable[["StatusBroadcaster"], _typing.Awaitable[None]]
1131
1156
  _SERVICE_REFRESH_CALLBACKS: _typing.List[_ServiceRefreshCallback] = []
1132
1157
 
1133
1158
  from services.plugin.registry import IdempotentList as _IdempotentList # noqa: E402
@@ -1135,9 +1160,7 @@ from services.plugin.registry import IdempotentList as _IdempotentList # noqa:
1135
1160
  # Backed by the module-level _SERVICE_REFRESH_CALLBACKS list so the
1136
1161
  # existing iterator at line 153 (``for callback in list(_SERVICE_REFRESH_CALLBACKS)``)
1137
1162
  # keeps working unchanged.
1138
- _SERVICE_REFRESH_FANOUT: _IdempotentList[_ServiceRefreshCallback] = _IdempotentList(
1139
- "service_refresh", items=_SERVICE_REFRESH_CALLBACKS
1140
- )
1163
+ _SERVICE_REFRESH_FANOUT: _IdempotentList[_ServiceRefreshCallback] = _IdempotentList("service_refresh", items=_SERVICE_REFRESH_CALLBACKS)
1141
1164
 
1142
1165
 
1143
1166
  def register_service_refresh(callback: _ServiceRefreshCallback) -> None: