machinaos 0.0.76 → 0.0.78

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 (393) hide show
  1. package/README.md +143 -107
  2. package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
  3. package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
  4. package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
  5. package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
  6. package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
  7. package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
  8. package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
  9. package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
  10. package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
  11. package/client/dist/assets/index-DQ0nwhec.js +257 -0
  12. package/client/dist/assets/index-DxmbVskS.css +1 -0
  13. package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
  14. package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
  15. package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
  16. package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
  17. package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
  18. package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
  19. package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
  20. package/client/dist/index.html +37 -3
  21. package/client/index.html +28 -1
  22. package/client/package.json +44 -40
  23. package/client/src/App.tsx +2 -0
  24. package/client/src/Dashboard.tsx +157 -45
  25. package/client/src/ParameterPanel.tsx +3 -5
  26. package/client/src/adapters/nodeSpecToDescription.ts +1 -0
  27. package/client/src/assets/icons/NodeIcon.tsx +32 -0
  28. package/client/src/assets/icons/index.ts +4 -0
  29. package/client/src/assets/icons/stripe.svg +1 -0
  30. package/client/src/assets/icons/themedGlyphs.ts +404 -0
  31. package/client/src/components/AIAgentNode.tsx +77 -53
  32. package/client/src/components/GenericNode.tsx +34 -52
  33. package/client/src/components/OutputPanel.tsx +64 -147
  34. package/client/src/components/ParameterRenderer.tsx +5 -3
  35. package/client/src/components/SkillEditorModal.tsx +9 -18
  36. package/client/src/components/SquareNode.tsx +97 -115
  37. package/client/src/components/StartNode.tsx +32 -42
  38. package/client/src/components/SvgFilterDefs.tsx +54 -0
  39. package/client/src/components/TeamMonitorNode.tsx +12 -14
  40. package/client/src/components/ToolkitNode.tsx +35 -60
  41. package/client/src/components/TriggerNode.tsx +43 -77
  42. package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
  43. package/client/src/components/credentials/CredentialsModal.tsx +98 -30
  44. package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
  45. package/client/src/components/credentials/catalogueAdapter.ts +17 -1
  46. package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
  47. package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
  48. package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
  49. package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
  50. package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
  51. package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
  52. package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
  53. package/client/src/components/credentials/types.ts +12 -2
  54. package/client/src/components/credentials/useCredentialPanel.ts +43 -19
  55. package/client/src/components/icons/AIProviderIcons.tsx +16 -0
  56. package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
  57. package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
  58. package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
  59. package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
  60. package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
  61. package/client/src/components/parameterPanel/InputSection.tsx +9 -7
  62. package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
  63. package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
  64. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
  65. package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
  66. package/client/src/components/ui/AIResultModal.tsx +1 -1
  67. package/client/src/components/ui/CollapsibleSection.tsx +9 -5
  68. package/client/src/components/ui/CommandPalette.tsx +147 -0
  69. package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
  70. package/client/src/components/ui/ComponentItem.tsx +13 -7
  71. package/client/src/components/ui/ComponentPalette.tsx +24 -13
  72. package/client/src/components/ui/ConsolePanel.tsx +19 -11
  73. package/client/src/components/ui/DropCap.tsx +28 -0
  74. package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
  75. package/client/src/components/ui/InputNodesPanel.tsx +1 -1
  76. package/client/src/components/ui/Modal.tsx +38 -6
  77. package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
  78. package/client/src/components/ui/SettingsPanel.tsx +42 -13
  79. package/client/src/components/ui/StatusBar.tsx +108 -0
  80. package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
  81. package/client/src/components/ui/TopToolbar.tsx +42 -25
  82. package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
  83. package/client/src/components/ui/action-button.tsx +40 -15
  84. package/client/src/components/ui/button.tsx +24 -1
  85. package/client/src/components/ui/dropdown-menu.tsx +24 -2
  86. package/client/src/components/ui/input.tsx +19 -2
  87. package/client/src/components/ui/select.tsx +15 -0
  88. package/client/src/components/ui/textarea.tsx +15 -2
  89. package/client/src/contexts/AuthContext.tsx +148 -109
  90. package/client/src/contexts/ThemeContext.tsx +93 -17
  91. package/client/src/contexts/WebSocketContext.tsx +373 -206
  92. package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
  93. package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
  94. package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
  95. package/client/src/hooks/useAppTheme.ts +209 -7
  96. package/client/src/hooks/useAutoSkillEdges.ts +7 -2
  97. package/client/src/hooks/useCatalogueQuery.ts +67 -1
  98. package/client/src/hooks/useDragVariable.ts +1 -1
  99. package/client/src/hooks/useNodeAllowlist.ts +115 -8
  100. package/client/src/hooks/useOnboarding.ts +20 -8
  101. package/client/src/hooks/useParameterPanel.ts +2 -1
  102. package/client/src/hooks/useReactFlowNodes.ts +2 -1
  103. package/client/src/hooks/useSound.ts +185 -0
  104. package/client/src/hooks/useWorkflowManagement.ts +6 -8
  105. package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
  106. package/client/src/index.css +65 -3
  107. package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
  108. package/client/src/lib/aiModelProviders.ts +8 -0
  109. package/client/src/lib/connectionConfig.ts +107 -0
  110. package/client/src/lib/queryPersist.ts +13 -5
  111. package/client/src/lib/sound.ts +393 -0
  112. package/client/src/main.tsx +20 -0
  113. package/client/src/store/useAppStore.ts +26 -0
  114. package/client/src/styles/canvasAnimations.ts +37 -36
  115. package/client/src/styles/theme.ts +36 -20
  116. package/client/src/test/setup.ts +1 -0
  117. package/client/src/themes/atomic.css +253 -0
  118. package/client/src/themes/base.css +373 -0
  119. package/client/src/themes/cyber.css +890 -0
  120. package/client/src/themes/dark.css +70 -0
  121. package/client/src/themes/edo.css +246 -0
  122. package/client/src/themes/greek.css +293 -0
  123. package/client/src/themes/light.css +78 -0
  124. package/client/src/themes/plague.css +253 -0
  125. package/client/src/themes/renaissance.css +727 -0
  126. package/client/src/themes/rot.css +249 -0
  127. package/client/src/themes/steampunk.css +272 -0
  128. package/client/src/themes/surveillance.css +289 -0
  129. package/client/src/themes/wasteland.css +250 -0
  130. package/client/src/types/INodeProperties.ts +5 -0
  131. package/client/src/types/NodeTypes.ts +11 -1
  132. package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
  133. package/client/src/types/cloudEvents.ts +78 -0
  134. package/client/src/vite-env.d.ts +7 -0
  135. package/client/tsconfig.json +1 -1
  136. package/client/vite.config.js +62 -2
  137. package/install.ps1 +1 -1
  138. package/install.sh +1 -1
  139. package/machina/commands/build.py +51 -7
  140. package/machina/pyproject.toml +4 -0
  141. package/machina/supervisor.py +12 -2
  142. package/machina/tree.py +71 -21
  143. package/package.json +4 -4
  144. package/scripts/install.js +16 -1
  145. package/server/config/ai_cli_providers.json +54 -0
  146. package/server/config/credential_providers.json +109 -2
  147. package/server/config/llm_defaults.json +24 -0
  148. package/server/config/model_registry.json +338 -499
  149. package/server/config/node_allowlist.json +16 -1
  150. package/server/config/pricing.json +8 -0
  151. package/server/constants.py +38 -15
  152. package/server/core/container.py +2 -2
  153. package/server/core/credentials_database.py +35 -2
  154. package/server/core/logging.py +4 -3
  155. package/server/main.py +99 -13
  156. package/server/models/node_metadata.py +1 -0
  157. package/server/nodejs/package.json +8 -6
  158. package/server/nodejs/src/index.ts +22 -5
  159. package/server/nodes/README.md +31 -4
  160. package/server/nodes/agent/_inline.py +2 -0
  161. package/server/nodes/agent/_specialized.py +6 -3
  162. package/server/nodes/agent/ai_agent.py +13 -3
  163. package/server/nodes/agent/chat_agent.py +6 -3
  164. package/server/nodes/agent/claude_code_agent.py +287 -75
  165. package/server/nodes/agent/codex_agent.py +239 -0
  166. package/server/nodes/agent/deep_agent.py +3 -3
  167. package/server/nodes/agent/rlm_agent.py +3 -3
  168. package/server/nodes/android/__init__.py +31 -1
  169. package/server/nodes/android/_base.py +9 -5
  170. package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
  171. package/server/nodes/android/_handlers.py +154 -0
  172. package/server/nodes/android/_option_loaders.py +44 -0
  173. package/server/nodes/android/_refresh.py +127 -0
  174. package/server/{services/android → nodes/android/_relay}/client.py +4 -4
  175. package/server/{routers/android.py → nodes/android/_router.py} +27 -8
  176. package/server/nodes/browser/browser.py +2 -2
  177. package/server/nodes/code/_base.py +6 -2
  178. package/server/nodes/code/_claude_code.py +134 -0
  179. package/server/nodes/document/embedding_generator.py +3 -3
  180. package/server/nodes/document/http_scraper.py +3 -3
  181. package/server/nodes/document/vector_store.py +5 -5
  182. package/server/nodes/email/__init__.py +11 -1
  183. package/server/nodes/email/_filters.py +21 -0
  184. package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
  185. package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
  186. package/server/nodes/email/email_read.py +1 -1
  187. package/server/nodes/email/email_receive.py +54 -5
  188. package/server/nodes/email/email_send.py +1 -1
  189. package/server/nodes/filesystem/shell.py +24 -1
  190. package/server/nodes/google/__init__.py +55 -1
  191. package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
  192. package/server/nodes/google/_base.py +2 -2
  193. package/server/nodes/google/_credentials.py +5 -5
  194. package/server/nodes/google/_filters.py +25 -0
  195. package/server/nodes/google/_handlers.py +57 -0
  196. package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
  197. package/server/nodes/google/_option_loaders.py +107 -0
  198. package/server/nodes/google/_refresh.py +66 -0
  199. package/server/nodes/google/_router.py +131 -0
  200. package/server/nodes/google/gmail_receive.py +41 -4
  201. package/server/nodes/groups.py +1 -0
  202. package/server/nodes/location/_credentials.py +45 -1
  203. package/server/{services/maps.py → nodes/location/_service.py} +18 -3
  204. package/server/nodes/location/gmaps_create.py +4 -4
  205. package/server/nodes/location/gmaps_locations.py +4 -4
  206. package/server/nodes/location/gmaps_nearby_places.py +4 -4
  207. package/server/nodes/model/_base.py +8 -3
  208. package/server/nodes/model/_credentials.py +96 -8
  209. package/server/nodes/model/_local_validator.py +345 -0
  210. package/server/nodes/model/lmstudio_chat_model.py +23 -0
  211. package/server/nodes/model/ollama_chat_model.py +25 -0
  212. package/server/nodes/proxy/_usage.py +2 -2
  213. package/server/nodes/proxy/proxy_config.py +14 -14
  214. package/server/nodes/proxy/proxy_request.py +4 -4
  215. package/server/nodes/scraper/_credentials.py +29 -1
  216. package/server/nodes/scraper/apify_actor.py +9 -9
  217. package/server/nodes/scraper/crawlee_scraper.py +5 -5
  218. package/server/nodes/search/brave_search.py +4 -0
  219. package/server/nodes/search/perplexity_search.py +9 -0
  220. package/server/nodes/search/serper_search.py +3 -0
  221. package/server/nodes/skill/simple_memory.py +12 -0
  222. package/server/nodes/social/_base.py +2 -2
  223. package/server/nodes/stripe/__init__.py +46 -0
  224. package/server/nodes/stripe/_credentials.py +33 -0
  225. package/server/nodes/stripe/_handlers.py +270 -0
  226. package/server/nodes/stripe/_install.py +127 -0
  227. package/server/nodes/stripe/_source.py +174 -0
  228. package/server/nodes/stripe/stripe_action.py +81 -0
  229. package/server/nodes/stripe/stripe_receive.py +92 -0
  230. package/server/nodes/telegram/_credentials.py +52 -1
  231. package/server/nodes/telegram/_handlers.py +19 -18
  232. package/server/nodes/telegram/_service.py +134 -32
  233. package/server/nodes/telegram/telegram_send.py +5 -6
  234. package/server/nodes/text/file_handler.py +2 -2
  235. package/server/nodes/text/text_generator.py +2 -2
  236. package/server/nodes/tool/agent_builder.py +630 -0
  237. package/server/nodes/tool/task_manager.py +144 -2
  238. package/server/nodes/twitter/__init__.py +38 -1
  239. package/server/nodes/twitter/_base.py +7 -7
  240. package/server/nodes/twitter/_credentials.py +1 -1
  241. package/server/nodes/twitter/_filters.py +37 -0
  242. package/server/nodes/twitter/_handlers.py +77 -0
  243. package/server/nodes/twitter/_oauth.py +124 -0
  244. package/server/nodes/twitter/_refresh.py +78 -0
  245. package/server/nodes/twitter/_router.py +29 -0
  246. package/server/nodes/twitter/twitter_receive.py +4 -0
  247. package/server/nodes/visuals.json +64 -19
  248. package/server/nodes/whatsapp/__init__.py +45 -5
  249. package/server/nodes/whatsapp/_base.py +3 -3
  250. package/server/nodes/whatsapp/_filters.py +137 -0
  251. package/server/nodes/whatsapp/_handlers.py +167 -0
  252. package/server/nodes/whatsapp/_option_loaders.py +68 -0
  253. package/server/nodes/whatsapp/_refresh.py +62 -0
  254. package/server/nodes/whatsapp/_runtime.py +1 -1
  255. package/server/pyproject.toml +29 -7
  256. package/server/routers/schemas.py +2 -2
  257. package/server/routers/webhook.py +26 -9
  258. package/server/routers/websocket.py +149 -810
  259. package/server/services/ai.py +89 -8
  260. package/server/services/auth.py +220 -43
  261. package/server/services/claude_oauth.py +126 -100
  262. package/server/services/cli_agent/__init__.py +78 -0
  263. package/server/services/cli_agent/_handlers.py +237 -0
  264. package/server/services/cli_agent/config.py +112 -0
  265. package/server/services/cli_agent/factory.py +48 -0
  266. package/server/services/cli_agent/lockfile.py +141 -0
  267. package/server/services/cli_agent/mcp_server.py +482 -0
  268. package/server/services/cli_agent/protocol.py +173 -0
  269. package/server/services/cli_agent/providers/__init__.py +9 -0
  270. package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
  271. package/server/services/cli_agent/providers/google_gemini.py +80 -0
  272. package/server/services/cli_agent/providers/openai_codex.py +310 -0
  273. package/server/services/cli_agent/service.py +607 -0
  274. package/server/services/cli_agent/session.py +618 -0
  275. package/server/services/cli_agent/types.py +227 -0
  276. package/server/services/cli_agent/workflow_tools.py +233 -0
  277. package/server/services/credential_registry.py +26 -1
  278. package/server/services/deployment/manager.py +26 -145
  279. package/server/services/deployment/poll_registry.py +59 -0
  280. package/server/services/event_waiter.py +76 -246
  281. package/server/services/events/__init__.py +54 -0
  282. package/server/services/events/cli.py +78 -0
  283. package/server/services/events/daemon.py +163 -0
  284. package/server/services/events/envelope.py +281 -0
  285. package/server/services/events/lifecycle.py +99 -0
  286. package/server/services/events/oauth_lifecycle.py +534 -0
  287. package/server/services/events/polling.py +60 -0
  288. package/server/services/events/push.py +36 -0
  289. package/server/services/events/source.py +63 -0
  290. package/server/services/events/triggers.py +118 -0
  291. package/server/services/events/verifiers/__init__.py +25 -0
  292. package/server/services/events/verifiers/base.py +28 -0
  293. package/server/services/events/verifiers/github.py +25 -0
  294. package/server/services/events/verifiers/hmac_basic.py +32 -0
  295. package/server/services/events/verifiers/standard_webhooks.py +47 -0
  296. package/server/services/events/verifiers/stripe.py +42 -0
  297. package/server/services/events/webhook.py +105 -0
  298. package/server/services/handlers/tools.py +28 -186
  299. package/server/services/llm/config.py +7 -0
  300. package/server/services/llm/factory.py +8 -2
  301. package/server/services/memory/__init__.py +52 -0
  302. package/server/services/memory/jsonl.py +80 -0
  303. package/server/services/memory/markdown.py +65 -0
  304. package/server/services/memory/state.py +112 -0
  305. package/server/services/memory/vector_store.py +40 -0
  306. package/server/services/model_registry.py +76 -0
  307. package/server/services/node_allowlist.py +71 -15
  308. package/server/services/node_executor.py +2 -2
  309. package/server/services/node_output_schemas.py +21 -10
  310. package/server/services/node_spec.py +1 -1
  311. package/server/services/oauth_utils.py +1 -1
  312. package/server/services/plugin/__init__.py +2 -0
  313. package/server/services/plugin/base.py +44 -2
  314. package/server/services/plugin/credential.py +288 -1
  315. package/server/services/plugin/deps.py +105 -0
  316. package/server/services/plugin/edge_walker.py +12 -4
  317. package/server/services/plugin/oauth.py +381 -0
  318. package/server/services/plugin/polling.py +247 -0
  319. package/server/services/plugin/registry.py +145 -0
  320. package/server/services/plugin/singleton.py +65 -0
  321. package/server/services/plugin/ws.py +81 -0
  322. package/server/services/process_service.py +31 -2
  323. package/server/services/status_broadcaster.py +155 -238
  324. package/server/services/temporal/workflow.py +7 -7
  325. package/server/services/workflow.py +21 -3
  326. package/server/services/ws_handler_registry.py +111 -28
  327. package/server/skills/GUIDE.md +16 -1
  328. package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
  329. package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
  330. package/server/tests/credentials/test_auth_service.py +16 -9
  331. package/server/tests/credentials/test_credential_broadcasts.py +219 -0
  332. package/server/tests/credentials/test_google_oauth.py +6 -6
  333. package/server/tests/credentials/test_oauth_utils.py +1 -1
  334. package/server/tests/credentials/test_twitter_oauth.py +2 -2
  335. package/server/tests/credentials/test_websocket_handlers.py +44 -20
  336. package/server/tests/llm/test_factory.py +1 -0
  337. package/server/tests/llm/test_wiring.py +5 -1
  338. package/server/tests/nodes/_compat.py +24 -24
  339. package/server/tests/nodes/test_agent_builder.py +439 -0
  340. package/server/tests/nodes/test_ai_tools.py +18 -14
  341. package/server/tests/nodes/test_code_fs_process.py +17 -8
  342. package/server/tests/nodes/test_email.py +10 -9
  343. package/server/tests/nodes/test_google_workspace.py +2 -2
  344. package/server/tests/nodes/test_specialized_agents.py +100 -53
  345. package/server/tests/nodes/test_stripe_plugin.py +293 -0
  346. package/server/tests/nodes/test_telegram_social.py +4 -4
  347. package/server/tests/nodes/test_twitter.py +1 -1
  348. package/server/tests/nodes/test_web_automation.py +2 -2
  349. package/server/tests/nodes/test_whatsapp.py +9 -9
  350. package/server/tests/services/cli_agent/__init__.py +0 -0
  351. package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
  352. package/server/tests/services/cli_agent/test_providers.py +358 -0
  353. package/server/tests/services/cli_agent/test_service.py +298 -0
  354. package/server/tests/services/memory/__init__.py +0 -0
  355. package/server/tests/services/memory/test_jsonl.py +188 -0
  356. package/server/tests/services/test_events.py +333 -0
  357. package/server/tests/test_node_spec.py +56 -16
  358. package/server/tests/test_plugin_helpers.py +116 -0
  359. package/server/tests/test_plugin_self_containment.py +486 -0
  360. package/server/tests/test_status_broadcasts.py +425 -0
  361. package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
  362. package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
  363. package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
  364. package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
  365. package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
  366. package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
  367. package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
  368. package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
  369. package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
  370. package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
  371. package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
  372. package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
  373. package/client/dist/assets/index-D9tZfgvi.js +0 -363
  374. package/client/dist/assets/index-al7snTkG.css +0 -1
  375. package/client/src/components/credentials/providers.tsx +0 -177
  376. package/server/routers/google.py +0 -277
  377. package/server/routers/maps.py +0 -142
  378. package/server/routers/twitter.py +0 -365
  379. package/server/services/claude_code_service.py +0 -106
  380. package/server/services/memory.py +0 -159
  381. package/server/services/node_option_loaders/__init__.py +0 -77
  382. package/server/services/node_option_loaders/android_loaders.py +0 -55
  383. package/server/services/node_option_loaders/google_loaders.py +0 -97
  384. package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
  385. package/server/services/twitter_oauth.py +0 -411
  386. package/server/services/websocket_client.py +0 -29
  387. /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
  388. /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
  389. /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
  390. /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
  391. /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
  392. /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
  393. /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
@@ -0,0 +1,118 @@
1
+ """Generic TriggerNode base classes backed by EventSources.
2
+
3
+ Plugins subclass :class:`WebhookTriggerNode` and declare:
4
+
5
+ type, display_name, group, ... (standard plugin metadata)
6
+ webhook_source: ClassVar[Type[WebhookSource]]
7
+ Params, Output (Pydantic models)
8
+ shape_output(event) (optional) reshape WorkflowEvent -> Output dict
9
+ _check_precondition() (optional) return error str if not ready
10
+ _extra_filter(params) (optional) additional filter on top of event_type
11
+
12
+ The base provides:
13
+ - event_type derived from webhook_source.type
14
+ - build_filter combining CloudEvents type-glob + _extra_filter
15
+ - execute() with precheck + shape passthrough
16
+ - the standard Operation("wait") stub
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import time
22
+ from typing import Any, Callable, ClassVar, Dict, Optional, Type
23
+
24
+ from pydantic import BaseModel, ConfigDict, Field
25
+
26
+ from services.plugin import NodeContext, Operation, TaskQueue, TriggerNode
27
+
28
+ from .envelope import WorkflowEvent
29
+ from .webhook import WebhookSource
30
+
31
+
32
+ class BaseTriggerParams(BaseModel):
33
+ """Default Params for WebhookTriggerNode subclasses. Subclass and add
34
+ provider-specific filters."""
35
+
36
+ event_type_filter: str = Field(
37
+ default="all",
38
+ description=(
39
+ "Event type to match. 'all' for every event, exact name, "
40
+ "or wildcard prefix (e.g. 'foo.*')."
41
+ ),
42
+ )
43
+
44
+ model_config = ConfigDict(extra="ignore")
45
+
46
+
47
+ class WebhookTriggerNode(TriggerNode):
48
+ """TriggerNode whose events arrive through a :class:`WebhookSource`.
49
+
50
+ The framework owns the wait/dispatch flow; subclasses describe only
51
+ the differences from the generic shape.
52
+ """
53
+
54
+ webhook_source: ClassVar[Optional[Type[WebhookSource]]] = None
55
+ # Auto-prepended to ``event_type_filter`` so users can type "charge.*"
56
+ # instead of "stripe.charge.*". Empty disables the convenience.
57
+ event_type_prefix: ClassVar[str] = ""
58
+ mode: ClassVar[str] = "event"
59
+ task_queue: ClassVar[str] = TaskQueue.TRIGGERS_EVENT
60
+ Params: ClassVar[Type[BaseModel]] = BaseTriggerParams
61
+
62
+ def __init_subclass__(cls, **kwargs):
63
+ super().__init_subclass__(**kwargs)
64
+ if cls.webhook_source is not None and not getattr(cls, "event_type", ""):
65
+ cls.event_type = cls.webhook_source.type
66
+
67
+ def build_filter(self, params: BaseModel) -> Callable[[Any], bool]:
68
+ type_filter = (getattr(params, "event_type_filter", "") or "all").strip()
69
+ if (
70
+ type_filter
71
+ and type_filter != "all"
72
+ and self.event_type_prefix
73
+ and not type_filter.startswith(self.event_type_prefix)
74
+ ):
75
+ type_filter = self.event_type_prefix + type_filter
76
+ extras = self._extra_filter(params)
77
+
78
+ def matches(event: Any) -> bool:
79
+ ev = event if isinstance(event, WorkflowEvent) else WorkflowEvent(**event)
80
+ if not ev.matches_type(type_filter):
81
+ return False
82
+ return extras(ev) if extras else True
83
+
84
+ return matches
85
+
86
+ def _extra_filter(self, params: BaseModel) -> Optional[Callable[[WorkflowEvent], bool]]:
87
+ """Override for filters beyond event-type matching (e.g. livemode)."""
88
+ return None
89
+
90
+ async def _check_precondition(self) -> Optional[str]:
91
+ """Override to short-circuit. Return error string or None."""
92
+ return None
93
+
94
+ async def execute(
95
+ self,
96
+ node_id: str,
97
+ parameters: Dict[str, Any],
98
+ context,
99
+ ) -> Dict[str, Any]:
100
+ err = await self._check_precondition()
101
+ if err:
102
+ return self._wrap_error(start_time=time.time(), error=err)
103
+ result = await super().execute(node_id, parameters, context)
104
+ if result.get("success"):
105
+ event = result.get("result") or {}
106
+ ev = event if isinstance(event, WorkflowEvent) else WorkflowEvent(**event)
107
+ result["result"] = self.shape_output(ev)
108
+ return result
109
+
110
+ def shape_output(self, event: WorkflowEvent) -> Dict[str, Any]:
111
+ """Default: dump the CloudEvent. Override for provider-shaped output."""
112
+ return event.model_dump(mode="json")
113
+
114
+ @Operation("wait")
115
+ async def wait(self, ctx: NodeContext, params: BaseModel):
116
+ raise NotImplementedError(
117
+ "Event triggers return via TriggerNode.execute, not the op body"
118
+ )
@@ -0,0 +1,25 @@
1
+ """Webhook signature verifiers — pluggable HMAC schemes per provider.
2
+
3
+ Each verifier is a stateless class with one method:
4
+
5
+ verify(headers, body, secret) -> None # raises ValueError on mismatch
6
+
7
+ Plugins reference verifier classes directly; there's no string-keyed
8
+ registry to avoid (yet another) lookup table.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from .base import WebhookVerifier
14
+ from .hmac_basic import HmacVerifier
15
+ from .stripe import StripeVerifier
16
+ from .standard_webhooks import StandardWebhooksVerifier
17
+ from .github import GitHubVerifier
18
+
19
+ __all__ = [
20
+ "WebhookVerifier",
21
+ "HmacVerifier",
22
+ "StripeVerifier",
23
+ "StandardWebhooksVerifier",
24
+ "GitHubVerifier",
25
+ ]
@@ -0,0 +1,28 @@
1
+ """Webhook verifier base — single-method contract."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Mapping
6
+
7
+
8
+ class WebhookVerifier:
9
+ """Abstract base. Each provider's signature scheme is one subclass."""
10
+
11
+ @classmethod
12
+ def verify(cls, headers: Mapping[str, str], body: bytes, secret: str) -> None:
13
+ """Raise ``ValueError`` if the request is not authentic.
14
+
15
+ ``headers`` is case-insensitive at the caller's discretion;
16
+ verifiers handle the lookup themselves to stay defensive.
17
+ ``body`` is the raw request bytes (signed payload).
18
+ ``secret`` is the provider-issued signing secret.
19
+ """
20
+ raise NotImplementedError
21
+
22
+ @staticmethod
23
+ def _header(headers: Mapping[str, str], name: str) -> str:
24
+ target = name.lower()
25
+ for k, v in headers.items():
26
+ if k.lower() == target:
27
+ return v
28
+ return ""
@@ -0,0 +1,25 @@
1
+ """GitHub webhook signature verifier.
2
+
3
+ Header: ``X-Hub-Signature-256: sha256=<hex_hmac>``
4
+ Algorithm: HMAC-SHA256 of the secret over the raw body, hex-encoded.
5
+ Reference: https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import hmac
12
+ from typing import Mapping
13
+
14
+ from .base import WebhookVerifier
15
+
16
+
17
+ class GitHubVerifier(WebhookVerifier):
18
+ @classmethod
19
+ def verify(cls, headers: Mapping[str, str], body: bytes, secret: str) -> None:
20
+ sig = cls._header(headers, "x-hub-signature-256")
21
+ if not sig.startswith("sha256="):
22
+ raise ValueError("X-Hub-Signature-256 missing or malformed")
23
+ expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
24
+ if not hmac.compare_digest(expected, sig):
25
+ raise ValueError("X-Hub-Signature-256 mismatch")
@@ -0,0 +1,32 @@
1
+ """Generic HMAC-SHA256 verifier — fallback for providers whose scheme
2
+ is "single header carrying hex HMAC of the raw body".
3
+
4
+ Subclasses customise :attr:`header_name` and (optionally)
5
+ :attr:`signature_prefix`. The default reads ``X-Signature-256`` and
6
+ expects a bare hex digest.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import hashlib
12
+ import hmac
13
+ from typing import ClassVar, Mapping
14
+
15
+ from .base import WebhookVerifier
16
+
17
+
18
+ class HmacVerifier(WebhookVerifier):
19
+ header_name: ClassVar[str] = "X-Signature-256"
20
+ signature_prefix: ClassVar[str] = "" # e.g. "sha256=" for some providers
21
+
22
+ @classmethod
23
+ def verify(cls, headers: Mapping[str, str], body: bytes, secret: str) -> None:
24
+ sig = cls._header(headers, cls.header_name)
25
+ if not sig:
26
+ raise ValueError(f"{cls.header_name} header missing")
27
+ if cls.signature_prefix and not sig.startswith(cls.signature_prefix):
28
+ raise ValueError(f"{cls.header_name} missing prefix {cls.signature_prefix!r}")
29
+ provided = sig[len(cls.signature_prefix):]
30
+ expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
31
+ if not hmac.compare_digest(expected, provided):
32
+ raise ValueError(f"{cls.header_name} mismatch")
@@ -0,0 +1,47 @@
1
+ """Standard Webhooks (Svix) signature verifier.
2
+
3
+ Spec: https://www.standardwebhooks.com/
4
+ Headers: ``webhook-id``, ``webhook-timestamp``, ``webhook-signature``
5
+ Signed payload: ``f"{webhook-id}.{webhook-timestamp}.{raw_body}"``
6
+ Algorithm: HMAC-SHA256 of the secret (b64-decoded after the ``whsec_`` prefix
7
+ is stripped) over the signed payload, base64-encoded.
8
+ ``webhook-signature`` may carry multiple space-separated ``v1,<sig>`` entries
9
+ to support secret rotation.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import base64
15
+ import hashlib
16
+ import hmac
17
+ from typing import Mapping
18
+
19
+ from .base import WebhookVerifier
20
+
21
+
22
+ class StandardWebhooksVerifier(WebhookVerifier):
23
+ @classmethod
24
+ def verify(cls, headers: Mapping[str, str], body: bytes, secret: str) -> None:
25
+ msg_id = cls._header(headers, "webhook-id")
26
+ timestamp = cls._header(headers, "webhook-timestamp")
27
+ sig_header = cls._header(headers, "webhook-signature")
28
+ if not (msg_id and timestamp and sig_header):
29
+ raise ValueError("Standard Webhooks headers missing")
30
+
31
+ if secret.startswith("whsec_"):
32
+ key = base64.b64decode(secret[len("whsec_"):])
33
+ else:
34
+ key = secret.encode()
35
+
36
+ signed = f"{msg_id}.{timestamp}.".encode() + body
37
+ expected = base64.b64encode(
38
+ hmac.new(key, signed, hashlib.sha256).digest()
39
+ ).decode()
40
+
41
+ candidates: list[str] = []
42
+ for part in sig_header.split():
43
+ if "," in part:
44
+ _, value = part.split(",", 1)
45
+ candidates.append(value)
46
+ if not any(hmac.compare_digest(expected, c) for c in candidates):
47
+ raise ValueError("Standard Webhooks signature mismatch")
@@ -0,0 +1,42 @@
1
+ """Stripe webhook signature verifier.
2
+
3
+ Header format: ``Stripe-Signature: t=<unix_ts>,v1=<hex_hmac>[,v1=<rotated>]``
4
+ Signed payload: ``f"{timestamp}.{raw_body}"`` (HMAC-SHA256, hex)
5
+ Reference: https://stripe.com/docs/webhooks/signatures
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import hmac
12
+ from typing import Mapping
13
+
14
+ from .base import WebhookVerifier
15
+
16
+
17
+ class StripeVerifier(WebhookVerifier):
18
+ @classmethod
19
+ def verify(cls, headers: Mapping[str, str], body: bytes, secret: str) -> None:
20
+ sig_header = cls._header(headers, "stripe-signature")
21
+ if not sig_header:
22
+ raise ValueError("Stripe-Signature header missing")
23
+
24
+ timestamp = ""
25
+ candidates: list[str] = []
26
+ for part in sig_header.split(","):
27
+ if "=" not in part:
28
+ continue
29
+ k, v = part.split("=", 1)
30
+ k = k.strip()
31
+ v = v.strip()
32
+ if k == "t":
33
+ timestamp = v
34
+ elif k == "v1":
35
+ candidates.append(v)
36
+ if not timestamp or not candidates:
37
+ raise ValueError("Stripe-Signature missing t= or v1= component")
38
+
39
+ signed = f"{timestamp}.".encode() + body
40
+ expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
41
+ if not any(hmac.compare_digest(expected, c) for c in candidates):
42
+ raise ValueError("Stripe-Signature mismatch")
@@ -0,0 +1,105 @@
1
+ """WebhookSource — push source backed by an HTTP POST to /webhook/{path}.
2
+
3
+ Plugins subclass and declare:
4
+
5
+ class MyWebhook(WebhookSource):
6
+ path = "myprovider"
7
+ verifier = MyVerifier # WebhookVerifier subclass (optional)
8
+ secret_field = "my_webhook_secret" # extra field on the credential
9
+
10
+ async def shape(self, request, body, payload) -> WorkflowEvent: ...
11
+
12
+ A single edit to ``routers/webhook.py`` consults :data:`WEBHOOK_SOURCES`
13
+ before the legacy generic dispatch path. No plugin name is hardcoded
14
+ in the router.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import json
20
+ from typing import ClassVar, Dict, Optional, Type
21
+
22
+ from fastapi import HTTPException, Request
23
+
24
+ from core.logging import get_logger
25
+
26
+ from .envelope import WorkflowEvent
27
+ from .push import PushEventSource
28
+ from .verifiers import WebhookVerifier
29
+
30
+ logger = get_logger(__name__)
31
+
32
+
33
+ WEBHOOK_SOURCES: Dict[str, "WebhookSource"] = {}
34
+
35
+
36
+ def register_webhook_source(source: "WebhookSource") -> None:
37
+ """Idempotent: same instance for the same path is a no-op; conflicts raise."""
38
+ existing = WEBHOOK_SOURCES.get(source.path)
39
+ if existing is not None and existing is not source:
40
+ raise ValueError(
41
+ f"Webhook path {source.path!r} already registered to {type(existing).__name__}"
42
+ )
43
+ WEBHOOK_SOURCES[source.path] = source
44
+
45
+
46
+ class WebhookSource(PushEventSource):
47
+ """Base for HTTP-webhook event sources.
48
+
49
+ Subclass contract:
50
+ path: URL fragment under /webhook/
51
+ verifier: WebhookVerifier subclass (or None to skip verification)
52
+ secret_field: credential extra-field name holding the signing secret
53
+ shape(): turn the verified request into a WorkflowEvent
54
+ """
55
+
56
+ path: ClassVar[str] = ""
57
+ verifier: ClassVar[Optional[Type[WebhookVerifier]]] = None
58
+ secret_field: ClassVar[Optional[str]] = None
59
+
60
+ async def _resolve_secret(self) -> Optional[str]:
61
+ if self.secret_field is None or self.credential is None:
62
+ return None
63
+ try:
64
+ secrets = await self.credential.resolve()
65
+ except PermissionError:
66
+ return None
67
+ return secrets.get(self.secret_field)
68
+
69
+ async def shape(self, request: Request, body: bytes, payload: dict) -> WorkflowEvent:
70
+ """Override to map the verified payload into a CloudEvent."""
71
+ return WorkflowEvent(
72
+ source=f"webhook://{self.path}",
73
+ type=f"webhook.{self.path}",
74
+ data=payload,
75
+ )
76
+
77
+ async def handle(self, request: Request) -> WorkflowEvent:
78
+ """Called by the webhook router. Verifies signature, parses the
79
+ body, shapes the event, dispatches into ``event_waiter``, and
80
+ returns the event for logging / response shaping."""
81
+ body = await request.body()
82
+ if self.verifier is not None:
83
+ secret = await self._resolve_secret()
84
+ if not secret:
85
+ logger.warning(
86
+ "[%s] no signing secret available; accepting unverified event",
87
+ self.type or self.path,
88
+ )
89
+ else:
90
+ try:
91
+ self.verifier.verify(dict(request.headers), body, secret)
92
+ except ValueError as e:
93
+ raise HTTPException(status_code=400, detail=str(e))
94
+
95
+ try:
96
+ payload = json.loads(body.decode() or "{}")
97
+ except (json.JSONDecodeError, UnicodeDecodeError) as e:
98
+ raise HTTPException(status_code=400, detail=f"invalid JSON body: {e}")
99
+
100
+ event = await self.shape(request, body, payload)
101
+ await self.receive(event)
102
+
103
+ from services import event_waiter
104
+ event_waiter.dispatch(self.type, event)
105
+ return event
@@ -145,8 +145,19 @@ async def _dispatch_tool(tool_name: str, tool_args: Dict[str, Any],
145
145
  node_type = config.get('node_type', '')
146
146
  node_params = config.get('parameters', {})
147
147
 
148
- # Execution context available to all tool handlers (workspace, etc.)
149
- context = {'workspace_dir': config.get('workspace_dir', '')}
148
+ # Execution context for tool handlers. Includes canvas state
149
+ # (nodes/edges/workflow_id) that callers (chat_tool_executor in
150
+ # ai.py / rlm adapter / deepagents adapter) plumb in via `config`.
151
+ # Tools that need canvas awareness (e.g. agentBuilder.inspect_canvas)
152
+ # read these via NodeContext.nodes / .edges / .workflow_id; tools
153
+ # that don't simply ignore them.
154
+ context = {
155
+ 'workspace_dir': config.get('workspace_dir', ''),
156
+ 'nodes': config.get('nodes', []),
157
+ 'edges': config.get('edges', []),
158
+ 'workflow_id': config.get('workflow_id'),
159
+ 'parent_node_id': config.get('parent_node_id'),
160
+ }
150
161
 
151
162
  logger.info("[Tool] Executing '%s' (node_type=%s, workspace=%s)", tool_name, node_type, context['workspace_dir'])
152
163
 
@@ -201,10 +212,12 @@ async def _dispatch_tool(tool_name: str, tool_args: Dict[str, Any],
201
212
  from nodes.android._base import execute_android_service_tool
202
213
  return await execute_android_service_tool(tool_args, config)
203
214
 
204
- # taskManager: dispatcher shares state with the agent-delegation
215
+ # taskManager: dispatcher reads through to the agent-delegation
205
216
  # tracking dicts (_delegated_tasks / _delegation_results) defined
206
- # below in this module, so the operation matrix stays here.
217
+ # below in this module. The operation matrix moved to the plugin
218
+ # (Wave 11.I, milestone O).
207
219
  if node_type == 'taskManager':
220
+ from nodes.tool.task_manager import _execute_task_manager
208
221
  return await _execute_task_manager(tool_args, config)
209
222
 
210
223
  # proxyConfig: 10-operation matrix lives on the plugin
@@ -242,34 +255,11 @@ async def _dispatch_tool(tool_name: str, tool_args: Dict[str, Any],
242
255
 
243
256
 
244
257
  # _execute_duckduckgo_search: Wave 11.C moved logic to
245
- # nodes/search/duckduckgo_search.py DuckDuckGoSearchNode.search(), but
246
- # some contract tests patch sys.modules['ddgs'] and call the flat
247
- # function directly. The shim below preserves the old (args, config) ->
248
- # dict signature so those tests stay plumbing-only.
249
- async def _execute_duckduckgo_search(
250
- args: Dict[str, Any],
251
- config: Dict[str, Any] = None,
252
- ) -> Dict[str, Any]:
253
- """Back-compat wrapper around the plugin search path."""
254
- config = config or {}
255
- query = str(args.get("query", "")).strip()
256
- if not query:
257
- return {"error": "No search query provided"}
258
- max_results = int(config.get("max_results", args.get("max_results", 5)))
259
- provider = config.get("provider", "duckduckgo")
260
- from ddgs import DDGS
261
- raw = list(DDGS().text(query, max_results=max_results))
262
- results = [
263
- {
264
- "title": item.get("title", ""),
265
- "snippet": item.get("body", ""),
266
- "url": item.get("href", ""),
267
- }
268
- for item in raw
269
- ]
270
- return {"query": query, "provider": provider, "results": results}
271
-
272
-
258
+ # nodes/search/duckduckgo_search.py DuckDuckGoSearchNode.search(). Wave
259
+ # 11.I milestone O moved the flat (args, config) -> dict shim used by
260
+ # contract tests to tests/nodes/_compat.py -- production code never
261
+ # called it.
262
+ #
273
263
  # Wave 11.D.9: _execute_whatsapp_{send,db} deleted. WhatsApp tool execution
274
264
  # now routes through the plugin fast-path (nodes/whatsapp/*.py).
275
265
 
@@ -779,160 +769,12 @@ async def _execute_check_delegated_tasks(args: Dict[str, Any],
779
769
  }
780
770
 
781
771
 
782
- async def _execute_task_manager(
783
- tool_args: Dict[str, Any],
784
- config: Dict[str, Any]
785
- ) -> Dict[str, Any]:
786
- """Execute task manager operations.
787
-
788
- Dual-purpose: works as AI tool (LLM fills args) or workflow node (uses params).
789
-
790
- Operations:
791
- - list_tasks: List all active and completed delegated tasks
792
- - get_task: Get status details for a specific task
793
- - mark_done: Remove a task from active tracking
794
-
795
- Args:
796
- tool_args: Arguments from LLM tool call (operation, task_id, status_filter)
797
- config: Tool configuration with node parameters
798
-
799
- Returns:
800
- Dict with operation results
801
- """
802
- # Merge tool_args with node parameters (tool_args takes precedence)
803
- params = config.get('parameters', {})
804
- operation = tool_args.get('operation') or params.get('operation', 'list_tasks')
805
- task_id = tool_args.get('task_id') or params.get('task_id')
806
- status_filter = tool_args.get('status_filter') or params.get('status_filter')
807
- database = config.get('database')
808
-
809
- logger.debug(f"[TaskManager] Operation: {operation}, task_id: {task_id}, filter: {status_filter}")
810
-
811
- if operation == 'list_tasks':
812
- # Collect all tasks from _delegated_tasks and _delegation_results
813
- tasks = []
814
-
815
- # Active tasks from asyncio.Task tracking
816
- for tid, task in _delegated_tasks.items():
817
- if task.done():
818
- try:
819
- if task.cancelled():
820
- status = 'cancelled'
821
- elif task.exception():
822
- status = 'error'
823
- else:
824
- status = 'completed'
825
- except Exception:
826
- status = 'completed'
827
- else:
828
- status = 'running'
829
-
830
- tasks.append({
831
- 'task_id': tid,
832
- 'status': status,
833
- 'active': True
834
- })
835
-
836
- # Completed tasks from in-memory cache
837
- for tid, result in _delegation_results.items():
838
- if tid not in [t['task_id'] for t in tasks]:
839
- tasks.append({
840
- 'task_id': tid,
841
- 'status': result.get('status', 'completed'),
842
- 'agent_name': result.get('agent_name'),
843
- 'result_summary': str(result.get('result', ''))[:200],
844
- 'active': False
845
- })
846
-
847
- # Apply status filter
848
- if status_filter:
849
- tasks = [t for t in tasks if t.get('status') == status_filter]
850
-
851
- return {
852
- 'success': True,
853
- 'operation': 'list_tasks',
854
- 'tasks': tasks,
855
- 'count': len(tasks),
856
- 'running': sum(1 for t in tasks if t.get('status') == 'running'),
857
- 'completed': sum(1 for t in tasks if t.get('status') == 'completed'),
858
- 'errors': sum(1 for t in tasks if t.get('status') == 'error')
859
- }
860
-
861
- elif operation == 'get_task':
862
- if not task_id:
863
- return {'success': False, 'error': 'task_id is required for get_task operation'}
864
-
865
- # Use existing get_delegated_task_status for detailed lookup
866
- result = await get_delegated_task_status(task_ids=[task_id], database=database)
867
- tasks = result.get('tasks', [])
868
-
869
- if not tasks:
870
- return {
871
- 'success': False,
872
- 'error': f'Task {task_id} not found',
873
- 'task_id': task_id
874
- }
875
-
876
- task_info = tasks[0]
877
- return {
878
- 'success': True,
879
- 'operation': 'get_task',
880
- 'task_id': task_id,
881
- 'status': task_info.get('status'),
882
- 'agent_name': task_info.get('agent_name'),
883
- 'result': task_info.get('result'),
884
- 'error': task_info.get('error')
885
- }
886
-
887
- elif operation == 'mark_done':
888
- if not task_id:
889
- return {'success': False, 'error': 'task_id is required for mark_done operation'}
890
-
891
- # Remove from active tracking
892
- removed = False
893
- if task_id in _delegated_tasks:
894
- del _delegated_tasks[task_id]
895
- removed = True
896
- if task_id in _delegation_results:
897
- del _delegation_results[task_id]
898
- removed = True
899
-
900
- return {
901
- 'success': True,
902
- 'operation': 'mark_done',
903
- 'task_id': task_id,
904
- 'removed': removed,
905
- 'message': f'Task {task_id} marked as done and removed from tracking' if removed else f'Task {task_id} was not in active tracking'
906
- }
907
-
908
- return {'success': False, 'error': f'Unknown operation: {operation}'}
909
-
910
-
911
- async def handle_task_manager(
912
- node_id: str,
913
- node_type: str,
914
- parameters: Dict[str, Any],
915
- context: Dict[str, Any]
916
- ) -> Dict[str, Any]:
917
- """Handle taskManager as workflow node.
918
-
919
- Wrapper for _execute_task_manager that conforms to the standard
920
- workflow node handler signature.
921
-
922
- Args:
923
- node_id: The node ID
924
- node_type: Should be 'taskManager'
925
- parameters: Node parameters (operation, task_id, status_filter)
926
- context: Execution context
927
-
928
- Returns:
929
- Task manager operation results
930
- """
931
- config = {
932
- 'parameters': parameters,
933
- 'database': context.get('database')
934
- }
935
- return await _execute_task_manager({}, config)
772
+ # _execute_task_manager + handle_task_manager: Wave 11.I milestone O moved
773
+ # the operation matrix to nodes/tool/task_manager.py. The plugin reads
774
+ # through to the delegation registry (``_delegated_tasks`` /
775
+ # ``_delegation_results`` / ``get_delegated_task_status``) which still
776
+ # lives in this module -- delegation lifecycle is genuine cross-cutting
777
+ # framework state. ``handle_task_manager`` had no callers and was deleted.
936
778
 
937
779
 
938
780
  # =============================================================================