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
@@ -62,9 +62,7 @@ class ProbeResult:
62
62
  extra: Dict[str, Any] = field(default_factory=dict)
63
63
 
64
64
 
65
- def classify_credential_error(
66
- exc: BaseException, *, display_name: str
67
- ) -> ProbeResult:
65
+ def classify_credential_error(exc: BaseException, *, display_name: str) -> ProbeResult:
68
66
  """Map a transport / SDK exception to a typed ``ProbeResult``.
69
67
 
70
68
  Single source of truth for credential-error → user-message mapping.
@@ -169,10 +167,7 @@ class Credential:
169
167
  # Idempotent on re-import — only warn on genuine conflict.
170
168
  existing = CREDENTIAL_REGISTRY[cls.id]
171
169
  if existing.__qualname__ != cls.__qualname__:
172
- raise ValueError(
173
- f"Credential id '{cls.id}' registered by "
174
- f"{existing.__qualname__} and now by {cls.__qualname__}"
175
- )
170
+ raise ValueError(f"Credential id '{cls.id}' registered by " f"{existing.__qualname__} and now by {cls.__qualname__}")
176
171
  CREDENTIAL_REGISTRY[cls.id] = cls
177
172
 
178
173
  @classmethod
@@ -183,6 +178,63 @@ class Credential:
183
178
  """
184
179
  raise NotImplementedError
185
180
 
181
+ # ---- F7: credential icon resolution -----------------------------
182
+ #
183
+ # Mirrors :func:`nodes._visuals.get_plugin_icon_path` for credentials.
184
+ # The frontend credential modal reads ``cls.icon`` strings of the
185
+ # form ``"asset:<key>"`` / ``"lobehub:<brand>"`` / emoji, OR fetches
186
+ # the SVG from the backend via ``GET /api/schemas/credentials/{provider}/icon``.
187
+ #
188
+ # Plugin-folder canonical convention (brand-name files):
189
+ # ``server/nodes/<plugin>/<cls.id>.svg``
190
+ #
191
+ # The credential icon lives directly inside its owner plugin's
192
+ # folder, named after the credential's brand (e.g.
193
+ # ``server/nodes/telegram/telegram.svg``). Distinguished from node
194
+ # icons by the absence of the ``icon`` / ``icon_<type>`` prefix —
195
+ # node icons follow that convention (see
196
+ # ``nodes/_visuals.py::get_plugin_icon_path``), credential icons
197
+ # don't. No ambiguity.
198
+ #
199
+ # Resolution order:
200
+ # 1. ``<plugin_folder>/<cls.id>.svg`` — co-located brand-name file.
201
+ # 2. ``server/credentials/icons/<cls.id>.svg`` — legacy central
202
+ # catalogue (pre-Wave 11.K; new credentials should ship the icon
203
+ # inside their plugin folder).
204
+ # 3. ``None`` — no backend icon; frontend falls back to ``cls.icon``.
205
+
206
+ @classmethod
207
+ def get_icon_path(cls) -> Optional["Path"]:
208
+ """Return the on-disk path to this credential's icon SVG, or None.
209
+
210
+ Lazy import of ``inspect`` + ``pathlib`` keeps the credential
211
+ module's import cost flat — both are stdlib so it's free.
212
+ """
213
+ from pathlib import Path as _Path
214
+ import inspect
215
+
216
+ if not cls.id:
217
+ return None
218
+
219
+ # 1. Co-located in the plugin folder, brand-name basename.
220
+ try:
221
+ cred_file = _Path(inspect.getfile(cls)).resolve()
222
+ except (TypeError, OSError):
223
+ cred_file = None
224
+ if cred_file is not None:
225
+ co_located = cred_file.parent / f"{cls.id}.svg"
226
+ if co_located.exists():
227
+ return co_located
228
+
229
+ # 2. Legacy central catalogue. ``server/`` resolves from this
230
+ # file: services/plugin/credential.py -> services/.. -> server/.
231
+ server_root = _Path(__file__).resolve().parent.parent.parent
232
+ central = server_root / "credentials" / "icons" / f"{cls.id}.svg"
233
+ if central.exists():
234
+ return central
235
+
236
+ return None
237
+
186
238
  @classmethod
187
239
  def inject(cls, secrets: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
188
240
  """Mutate an httpx request dict (headers / params / json / auth)
@@ -286,9 +338,7 @@ class Credential:
286
338
  ``openai.OpenAIError`` to let the base map them to a typed
287
339
  message via :func:`classify_credential_error`.
288
340
  """
289
- raise NotImplementedError(
290
- f"Credential subclass {cls.__name__} must override _probe()"
291
- )
341
+ raise NotImplementedError(f"Credential subclass {cls.__name__} must override _probe()")
292
342
 
293
343
 
294
344
  class OAuth2Credential(Credential):
@@ -307,8 +357,8 @@ class OAuth2Credential(Credential):
307
357
  token_header: ClassVar[str] = "Authorization"
308
358
  token_prefix: ClassVar[str] = "Bearer "
309
359
  # Keys the user enters in credentials modal (API-key style rows)
310
- client_id_api_key: ClassVar[str] = "" # e.g. "google_client_id"
311
- client_secret_api_key: ClassVar[str] = "" # e.g. "google_client_secret"
360
+ client_id_api_key: ClassVar[str] = "" # e.g. "google_client_id"
361
+ client_secret_api_key: ClassVar[str] = "" # e.g. "google_client_secret"
312
362
 
313
363
  @classmethod
314
364
  async def resolve(cls, *, user_id: str = "owner") -> Dict[str, Any]:
@@ -317,9 +367,16 @@ class OAuth2Credential(Credential):
317
367
  auth_service = container.auth_service()
318
368
  tokens = await auth_service.get_oauth_tokens(cls.id, user_id)
319
369
  if not tokens or not tokens.get("access_token"):
320
- raise PermissionError(
321
- f"No OAuth tokens for '{cls.id}'. Connect via Credentials modal."
322
- )
370
+ # Annotate the exception so BaseNode.execute can surface the
371
+ # failing provider in the response envelope and emit a
372
+ # CloudEvents-typed broadcast (credential.oauth.runtime_failed)
373
+ # via broadcast_credential_event. Plain attribute assignment —
374
+ # no new exception class.
375
+ err = PermissionError(f"No OAuth tokens for '{cls.id}'. Connect via Credentials modal.")
376
+ err.provider = cls.id
377
+ err.reason = "missing"
378
+ err.auth = cls.auth # "oauth2"
379
+ raise err
323
380
  return tokens
324
381
 
325
382
  @classmethod
@@ -360,7 +417,7 @@ class ApiKeyCredential(Credential):
360
417
 
361
418
  auth: ClassVar[Literal["api_key"]] = "api_key"
362
419
  # Where the key goes
363
- key_name: ClassVar[str] = "" # header name or query-string key
420
+ key_name: ClassVar[str] = "" # header name or query-string key
364
421
  key_location: ClassVar[Literal["header", "query", "bearer"]] = "header"
365
422
  # Extra fields stored alongside (e.g. "apify_account_id" for Apify)
366
423
  extra_fields: ClassVar[Sequence[str]] = ()
@@ -373,7 +430,7 @@ class ApiKeyCredential(Credential):
373
430
  # ``httpx.HTTPStatusError`` / ``TimeoutException`` / ``ConnectError``
374
431
  # propagate to :meth:`Credential.validate`, where
375
432
  # :func:`classify_credential_error` produces the user-facing message.
376
- probe_url: ClassVar[str] = "" # set to enable the default probe
433
+ probe_url: ClassVar[str] = "" # set to enable the default probe
377
434
  probe_method: ClassVar[str] = "GET"
378
435
  probe_params: ClassVar[Dict[str, Any]] = {}
379
436
  probe_json: ClassVar[Optional[Dict[str, Any]]] = None
@@ -386,9 +443,11 @@ class ApiKeyCredential(Credential):
386
443
  auth_service = container.auth_service()
387
444
  api_key = await auth_service.get_api_key(cls.id)
388
445
  if not api_key:
389
- raise PermissionError(
390
- f"No API key for '{cls.id}'. Add via Credentials modal."
391
- )
446
+ err = PermissionError(f"No API key for '{cls.id}'. Add via Credentials modal.")
447
+ err.provider = cls.id
448
+ err.reason = "missing"
449
+ err.auth = cls.auth # "api_key"
450
+ raise err
392
451
  secrets: Dict[str, Any] = {"api_key": api_key}
393
452
  for field in cls.extra_fields:
394
453
  value = await auth_service.get_api_key(field)
@@ -427,8 +486,7 @@ class ApiKeyCredential(Credential):
427
486
  """
428
487
  if not cls.probe_url:
429
488
  raise NotImplementedError(
430
- f"Credential subclass {cls.__name__} must override _probe() "
431
- f"or set the `probe_url` class attribute"
489
+ f"Credential subclass {cls.__name__} must override _probe() " f"or set the `probe_url` class attribute"
432
490
  )
433
491
 
434
492
  request = cls.inject(
@@ -3,8 +3,8 @@
3
3
  Pure functions extracted from ``services/handlers/ai.py`` so every
4
4
  agent plugin can call them without depending on the legacy handler
5
5
  module. Used by :class:`AIAgentNode`, :class:`ChatAgentNode`,
6
- :class:`SpecializedAgentBase`, :class:`DeepAgentNode`,
7
- :class:`RLMAgentNode`, :class:`ClaudeCodeAgentNode`.
6
+ :class:`SpecializedAgentBase`, :class:`RLMAgentNode`,
7
+ :class:`ClaudeCodeAgentNode`.
8
8
 
9
9
  Three helpers:
10
10
 
@@ -14,18 +14,26 @@ Three helpers:
14
14
  ``masterSkill`` expansion + the ``androidTool`` Sub-Node pattern +
15
15
  child-agent tool discovery.
16
16
  - :func:`collect_teammate_connections` — walks ``input-teammates``
17
- edges (orchestrator / ai_employee / deep_agent team-lead pattern).
17
+ edges (orchestrator / ai_employee team-lead pattern).
18
18
  - :func:`format_task_context` — renders ``taskTrigger`` payload as a
19
19
  prompt prepend block.
20
20
 
21
+ Wave 11.I, X3: the Master-Skill expansion logic moved out of this
22
+ module (it used to ``from services.skill_loader import get_skill_loader``
23
+ inline). The :mod:`nodes.skill` package registers an expander callback
24
+ via :func:`register_master_skill_expander` from its ``__init__.py``;
25
+ edge_walker calls the registered callback instead of importing
26
+ ``skill_loader`` directly.
27
+
21
28
  These functions have **no service dependencies** beyond the
22
- ``Database`` instance. They mutate nothing. Wave 11.D.6 inlines the
23
- old ``handlers/ai.py:_collect_*`` shims to call here directly.
29
+ ``Database`` instance and the registered expander callback. They
30
+ mutate nothing. Wave 11.D.6 inlines the old ``handlers/ai.py:_collect_*``
31
+ shims to call here directly.
24
32
  """
25
33
 
26
34
  from __future__ import annotations
27
35
 
28
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
36
+ from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Tuple
29
37
 
30
38
  from constants import AI_AGENT_TYPES, ANDROID_SERVICE_NODE_TYPES
31
39
  from core.logging import get_logger
@@ -36,17 +44,52 @@ if TYPE_CHECKING:
36
44
  logger = get_logger(__name__)
37
45
 
38
46
 
47
+ # ---- Master-Skill expander callback registry ----------------------------
48
+
49
+ MasterSkillExpander = Callable[
50
+ [str, Dict[str, Any]],
51
+ Awaitable[List[Dict[str, Any]]],
52
+ ]
53
+ """Async callable that expands a Master Skill node's ``skills_config``
54
+ into a list of per-skill entries (the same shape ``_append_skill_entries``
55
+ produces for non-master skills). Signature:
56
+ ``(source_node_id, skills_config) -> list[entry]``."""
57
+
58
+ _MASTER_SKILL_EXPANDER: Optional[MasterSkillExpander] = None
59
+
60
+
61
+ def register_master_skill_expander(fn: MasterSkillExpander) -> None:
62
+ """Publish the Master-Skill expander callback.
63
+
64
+ Idempotent on equality. The :mod:`nodes.skill` plugin package
65
+ registers its expander from ``__init__.py`` on package import.
66
+ Edge-walking code calls :func:`get_master_skill_expander` and runs
67
+ whatever's registered; the framework has no skill_loader coupling.
68
+ """
69
+ global _MASTER_SKILL_EXPANDER
70
+ if _MASTER_SKILL_EXPANDER is not None and _MASTER_SKILL_EXPANDER != fn:
71
+ raise ValueError("register_master_skill_expander: callback already registered " "by a different callable; refusing to overwrite")
72
+ _MASTER_SKILL_EXPANDER = fn
73
+
74
+
75
+ def get_master_skill_expander() -> Optional[MasterSkillExpander]:
76
+ """Return the registered expander callback, or None when no skill
77
+ plugin has wired it (in which case Master-Skill nodes silently
78
+ expand to no entries -- the agent still runs without skills)."""
79
+ return _MASTER_SKILL_EXPANDER
80
+
81
+
39
82
  async def collect_agent_connections(
40
83
  node_id: str,
41
84
  context: Dict[str, Any],
42
85
  database: "Database",
43
86
  log_prefix: str = "[Agent]",
44
87
  ) -> Tuple[
45
- Optional[Dict[str, Any]], # memory_data
46
- List[Dict[str, Any]], # skill_data
47
- List[Dict[str, Any]], # tool_data
48
- Optional[Dict[str, Any]], # input_data
49
- Optional[Dict[str, Any]], # task_data
88
+ Optional[Dict[str, Any]], # memory_data
89
+ List[Dict[str, Any]], # skill_data
90
+ List[Dict[str, Any]], # tool_data
91
+ Optional[Dict[str, Any]], # input_data
92
+ Optional[Dict[str, Any]], # task_data
50
93
  ]:
51
94
  """Walk edges targeting ``node_id`` and collect everything an agent
52
95
  needs from its connected nodes.
@@ -80,10 +123,7 @@ async def collect_agent_connections(
80
123
  edge_targets = set(e.get("target") for e in edges)
81
124
  logger.debug(f"{log_prefix} All edge targets in graph: {edge_targets}")
82
125
  for e in incoming_edges:
83
- logger.debug(
84
- f"{log_prefix} Edge: source={e.get('source')}, "
85
- f"targetHandle={e.get('targetHandle')}"
86
- )
126
+ logger.debug(f"{log_prefix} Edge: source={e.get('source')}, " f"targetHandle={e.get('targetHandle')}")
87
127
 
88
128
  tool_incoming = [e for e in incoming_edges if e.get("targetHandle") == "input-tools"]
89
129
  logger.info(f"{log_prefix} Tool edges (input-tools handle): {len(tool_incoming)}")
@@ -101,18 +141,30 @@ async def collect_agent_connections(
101
141
  if target_handle == "input-memory":
102
142
  if source_node.get("type") == "simpleMemory":
103
143
  memory_data = await _build_memory_entry(
104
- node_id, source_node_id, database, log_prefix,
144
+ node_id,
145
+ source_node_id,
146
+ database,
147
+ log_prefix,
105
148
  )
106
149
 
107
150
  elif target_handle == "input-skill":
108
151
  await _append_skill_entries(
109
- source_node, source_node_id, database, skill_data, log_prefix,
152
+ source_node,
153
+ source_node_id,
154
+ database,
155
+ skill_data,
156
+ log_prefix,
110
157
  )
111
158
 
112
159
  elif target_handle == "input-tools":
113
160
  await _append_tool_entry(
114
- source_node, source_node_id, edges, nodes, database,
115
- tool_data, log_prefix,
161
+ source_node,
162
+ source_node_id,
163
+ edges,
164
+ nodes,
165
+ database,
166
+ tool_data,
167
+ log_prefix,
116
168
  )
117
169
 
118
170
  elif target_handle in ("input-main", "input-chat") or target_handle is None:
@@ -126,7 +178,10 @@ async def collect_agent_connections(
126
178
 
127
179
  elif target_handle == "input-task":
128
180
  task_data = await _resolve_task_payload(
129
- source_node_id, source_node, context, log_prefix,
181
+ source_node_id,
182
+ source_node,
183
+ context,
184
+ log_prefix,
130
185
  )
131
186
 
132
187
  logger.info(
@@ -164,7 +219,8 @@ async def _build_memory_entry(
164
219
  "session_id": session_id,
165
220
  "window_size": int(memory_params.get("window_size", 10)),
166
221
  "memory_content": memory_params.get(
167
- "memory_content", "# Conversation History\n\n*No messages yet.*\n",
222
+ "memory_content",
223
+ "# Conversation History\n\n*No messages yet.*\n",
168
224
  ),
169
225
  "long_term_enabled": memory_params.get("long_term_enabled", False),
170
226
  "retrieval_count": int(memory_params.get("retrieval_count", 3)),
@@ -175,9 +231,10 @@ async def _build_memory_entry(
175
231
  "last_session_id": memory_params.get("last_session_id"),
176
232
  }
177
233
  logger.info(
178
- "%s Connected memory node: node=%s session=%s (auto=%s) "
179
- "content_length=%d last_session_id=%s",
180
- log_prefix, memory_node_id, session_id,
234
+ "%s Connected memory node: node=%s session=%s (auto=%s) " "content_length=%d last_session_id=%s",
235
+ log_prefix,
236
+ memory_node_id,
237
+ session_id,
181
238
  not configured_session or configured_session == "default",
182
239
  len(entry["memory_content"]),
183
240
  entry["last_session_id"],
@@ -196,45 +253,31 @@ async def _append_skill_entries(
196
253
  skill_params = await database.get_node_parameters(source_node_id) or {}
197
254
 
198
255
  if skill_type == "masterSkill":
199
- from services.skill_loader import get_skill_loader
256
+ expander = get_master_skill_expander()
257
+ if expander is None:
258
+ logger.warning(
259
+ f"{log_prefix} Master Skill node found but no expander "
260
+ "registered. Skipping expansion -- ensure nodes.skill "
261
+ "is on the import path."
262
+ )
263
+ return
200
264
 
201
265
  skills_config = skill_params.get("skills_config", {})
202
266
  logger.debug(f"{log_prefix} Master Skill found with {len(skills_config)} configured skills")
203
- skill_loader = get_skill_loader()
204
-
205
- for skill_key, skill_cfg in skills_config.items():
206
- if not skill_cfg.get("enabled", False):
207
- continue
208
- instructions = skill_cfg.get("instructions", "")
209
- if instructions:
210
- logger.debug(f"{log_prefix} Using DB instructions for {skill_key}")
211
- else:
212
- try:
213
- skill = skill_loader.load_skill(skill_key)
214
- if skill:
215
- instructions = skill.instructions
216
- logger.debug(
217
- f"{log_prefix} Fallback: loaded instructions from skill folder for {skill_key}"
218
- )
219
- except Exception as e:
220
- logger.warning(f"{log_prefix} Failed to load skill {skill_key}: {e}")
221
-
222
- skill_data.append({
223
- "node_id": f"{source_node_id}_{skill_key}",
224
- "node_type": "masterSkill",
225
- "skill_name": skill_key,
226
- "parameters": {"instructions": instructions, "skillName": skill_key},
227
- "label": skill_key,
228
- })
229
- logger.debug(f"{log_prefix} Master Skill enabled: {skill_key}")
267
+ entries = await expander(source_node_id, skills_config)
268
+ skill_data.extend(entries)
269
+ for entry in entries:
270
+ logger.debug(f"{log_prefix} Master Skill enabled: {entry['skill_name']}")
230
271
  else:
231
- skill_data.append({
232
- "node_id": source_node_id,
233
- "node_type": skill_type,
234
- "skill_name": skill_params.get("skill_name", skill_type),
235
- "parameters": skill_params,
236
- "label": source_node.get("data", {}).get("label", skill_type),
237
- })
272
+ skill_data.append(
273
+ {
274
+ "node_id": source_node_id,
275
+ "node_type": skill_type,
276
+ "skill_name": skill_params.get("skill_name", skill_type),
277
+ "parameters": skill_params,
278
+ "label": source_node.get("data", {}).get("label", skill_type),
279
+ }
280
+ )
238
281
  logger.debug(f"{log_prefix} Connected skill: {skill_type}")
239
282
 
240
283
 
@@ -265,27 +308,23 @@ async def _append_tool_entry(
265
308
  continue
266
309
  service_target_handle = service_edge.get("targetHandle")
267
310
  if service_target_handle is not None and service_target_handle != "input-main":
268
- logger.debug(
269
- f"{log_prefix} Android Toolkit: Skipping edge with "
270
- f"targetHandle: {service_target_handle}"
271
- )
311
+ logger.debug(f"{log_prefix} Android Toolkit: Skipping edge with " f"targetHandle: {service_target_handle}")
272
312
  continue
273
313
  android_node_id = service_edge.get("source")
274
314
  android_node = next((n for n in nodes if n.get("id") == android_node_id), None)
275
315
  if android_node and android_node.get("type") in ANDROID_SERVICE_NODE_TYPES:
276
316
  android_params = await database.get_node_parameters(android_node_id) or {}
277
- connected_services.append({
278
- "node_id": android_node_id,
279
- "node_type": android_node.get("type"),
280
- "service_id": android_params.get("service_id"),
281
- "action": android_params.get("action"),
282
- "parameters": android_params,
283
- "label": android_node.get("data", {}).get("label", android_node.get("type")),
284
- })
285
- logger.debug(
286
- f"{log_prefix} Android toolkit connected service: "
287
- f"{android_params.get('service_id')}"
317
+ connected_services.append(
318
+ {
319
+ "node_id": android_node_id,
320
+ "node_type": android_node.get("type"),
321
+ "service_id": android_params.get("service_id"),
322
+ "action": android_params.get("action"),
323
+ "parameters": android_params,
324
+ "label": android_node.get("data", {}).get("label", android_node.get("type")),
325
+ }
288
326
  )
327
+ logger.debug(f"{log_prefix} Android toolkit connected service: " f"{android_params.get('service_id')}")
289
328
  tool_entry["connected_services"] = connected_services
290
329
  logger.debug(f"{log_prefix} Android toolkit has {len(connected_services)} connected services")
291
330
 
@@ -309,8 +348,7 @@ async def _append_tool_entry(
309
348
  child_tool_id = child_edge.get("source")
310
349
  child_tool_node = next((n for n in nodes if n.get("id") == child_tool_id), None)
311
350
  logger.debug(
312
- f"{log_prefix} Child agent {source_node_id}: "
313
- f"tool edge from {child_tool_id}, node found: {child_tool_node is not None}"
351
+ f"{log_prefix} Child agent {source_node_id}: " f"tool edge from {child_tool_id}, node found: {child_tool_node is not None}"
314
352
  )
315
353
  if child_tool_node:
316
354
  child_tool_type = child_tool_node.get("type", "")
@@ -318,10 +356,7 @@ async def _append_tool_entry(
318
356
  child_tools.append({"node_type": child_tool_type, "label": child_tool_label})
319
357
  if child_tools:
320
358
  tool_entry["child_tools"] = child_tools
321
- logger.debug(
322
- f"{log_prefix} Child agent {source_node_id} has tools: "
323
- f"{[t['label'] for t in child_tools]}"
324
- )
359
+ logger.debug(f"{log_prefix} Child agent {source_node_id} has tools: " f"{[t['label'] for t in child_tools]}")
325
360
 
326
361
  tool_data.append(tool_entry)
327
362
  logger.debug(f"{log_prefix} Connected tool: {tool_type}")
@@ -336,9 +371,7 @@ async def _resolve_task_payload(
336
371
  logger.info(f"{log_prefix} Found input-task edge from {source_node_id} (type={source_node.get('type')})")
337
372
 
338
373
  source_output = context.get("outputs", {}).get(source_node_id)
339
- logger.info(
340
- f"{log_prefix} Context outputs check for {source_node_id}: {source_output is not None}"
341
- )
374
+ logger.info(f"{log_prefix} Context outputs check for {source_node_id}: {source_output is not None}")
342
375
 
343
376
  if not source_output:
344
377
  get_output_fn = context.get("get_output_fn")
@@ -350,9 +383,7 @@ async def _resolve_task_payload(
350
383
  except Exception as e:
351
384
  logger.warning(f"{log_prefix} Failed to get output from DB: {e}")
352
385
  else:
353
- logger.warning(
354
- f"{log_prefix} No get_output_fn in context, cannot retrieve task output"
355
- )
386
+ logger.warning(f"{log_prefix} No get_output_fn in context, cannot retrieve task output")
356
387
 
357
388
  logger.info(
358
389
  f"{log_prefix} Source output for {source_node_id}: {source_output is not None}, "
@@ -361,11 +392,7 @@ async def _resolve_task_payload(
361
392
  if not source_output:
362
393
  return None
363
394
 
364
- if (
365
- isinstance(source_output, dict)
366
- and "result" in source_output
367
- and isinstance(source_output.get("result"), dict)
368
- ):
395
+ if isinstance(source_output, dict) and "result" in source_output and isinstance(source_output.get("result"), dict):
369
396
  task_data = source_output.get("result")
370
397
  logger.info(f"{log_prefix} Extracted nested task_data from result key")
371
398
  else:
@@ -385,7 +412,7 @@ async def collect_teammate_connections(
385
412
  ) -> List[Dict[str, Any]]:
386
413
  """Walk ``input-teammates`` edges and return connected agents.
387
414
 
388
- Used by ``orchestrator_agent`` / ``ai_employee`` / ``deep_agent``.
415
+ Used by ``orchestrator_agent`` / ``ai_employee``.
389
416
  """
390
417
  nodes = context.get("nodes", [])
391
418
  edges = context.get("edges", [])
@@ -402,12 +429,14 @@ async def collect_teammate_connections(
402
429
  if node_type not in AI_AGENT_TYPES:
403
430
  continue
404
431
  params = await database.get_node_parameters(source_id) or {}
405
- teammates.append({
406
- "node_id": source_id,
407
- "node_type": node_type,
408
- "label": source_node.get("data", {}).get("label", node_type),
409
- "parameters": params,
410
- })
432
+ teammates.append(
433
+ {
434
+ "node_id": source_id,
435
+ "node_type": node_type,
436
+ "label": source_node.get("data", {}).get("label", node_type),
437
+ "parameters": params,
438
+ }
439
+ )
411
440
  logger.debug(f"[Teams] Found teammate: {node_type} ({source_id})")
412
441
 
413
442
  return teammates
@@ -447,10 +476,4 @@ def format_task_context(task_data: Dict[str, Any]) -> str:
447
476
  "Report this error to the user and suggest next steps if appropriate."
448
477
  )
449
478
 
450
- return (
451
- f"Task update received:\n"
452
- f"- Agent: {agent_name}\n"
453
- f"- Task ID: {task_id}\n"
454
- f"- Status: {status}\n"
455
- f"- Data: {task_data}"
456
- )
479
+ return f"Task update received:\n" f"- Agent: {agent_name}\n" f"- Task ID: {task_id}\n" f"- Status: {status}\n" f"- Data: {task_data}"
@@ -0,0 +1,48 @@
1
+ """Shape validation for plugin identifiers used in URL routes + lookups.
2
+
3
+ Plugin node types (and credential provider ids) follow Python-identifier
4
+ rules: a letter or underscore followed by word characters. URL-facing
5
+ routes (``GET /api/schemas/nodes/{node_type}/icon`` etc.) and internal
6
+ helpers (``nodes._visuals.get_plugin_icon_path`` etc.) both validate
7
+ against this constraint to reject path-traversal injections at the
8
+ boundary.
9
+
10
+ Why this lives here (not inline at each callsite):
11
+
12
+ - One source of truth for the regex pattern. FastAPI consumes the raw
13
+ pattern string via ``Path(pattern=...)``; Python code consumes the
14
+ pre-compiled validator via :func:`is_valid_node_type`. Keeping the
15
+ pattern in one module guarantees the route-level and function-level
16
+ checks stay in lockstep.
17
+ - Recognised by CodeQL's ``py/path-injection`` taint analysis as a
18
+ sanitizer — the canonical "regex fullmatch on user input" pattern
19
+ (see https://codeql.github.com/codeql-query-help/python/py-path-injection/).
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import re
25
+ from typing import Final
26
+
27
+
28
+ # Letter/underscore then word chars — the shape every registered
29
+ # ``BaseNode.type`` and ``Credential.id`` follows. Exposed as a raw
30
+ # string so FastAPI's ``Path(pattern=...)`` / ``Query(pattern=...)``
31
+ # can consume it directly.
32
+ NODE_TYPE_PATTERN: Final[str] = r"^[A-Za-z_][A-Za-z0-9_]*$"
33
+
34
+ _NODE_TYPE_RE: Final[re.Pattern[str]] = re.compile(NODE_TYPE_PATTERN)
35
+
36
+
37
+ def is_valid_node_type(value: str) -> bool:
38
+ """True if ``value`` matches :data:`NODE_TYPE_PATTERN`.
39
+
40
+ Treats ``None`` / non-string input as invalid (defensive — the
41
+ type hint is ``str`` but callers may forward URL params verbatim).
42
+ """
43
+ if not isinstance(value, str):
44
+ return False
45
+ return bool(_NODE_TYPE_RE.fullmatch(value))
46
+
47
+
48
+ __all__ = ["NODE_TYPE_PATTERN", "is_valid_node_type"]
@@ -22,7 +22,7 @@ class InterceptorInput:
22
22
  node_id: str
23
23
  node_type: str
24
24
  parameters: Dict[str, Any]
25
- context: Any # NodeContext (avoid cycle in type hint)
25
+ context: Any # NodeContext (avoid cycle in type hint)
26
26
 
27
27
 
28
28
  class Interceptor(ABC):