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
@@ -41,6 +41,12 @@ class AIAgentParams(BaseModel):
41
41
  provider: Literal[
42
42
  "openai", "anthropic", "gemini", "openrouter",
43
43
  "groq", "cerebras", "deepseek", "kimi", "mistral",
44
+ # Local-server providers — agent execution reads
45
+ # ``{provider}_proxy`` to point the LangChain client at the
46
+ # user's localhost server. Without these entries the dropdown
47
+ # silently falls back to ``"openai"`` and execute_agent ends
48
+ # up calling api.openai.com instead.
49
+ "ollama", "lmstudio",
44
50
  ] = "openai"
45
51
  model: str = Field(
46
52
  default="", json_schema_extra={"placeholder": "Select a model..."},
@@ -114,18 +120,22 @@ class AIAgentNode(ActionNode):
114
120
  underlying LangGraph orchestration + tool binding + memory
115
121
  I/O + streaming hooks stay in AIService.
116
122
  """
117
- from core.container import container
123
+ from services.plugin.deps import get_ai_service, get_database
118
124
 
119
125
  from ._inline import prepare_agent_call
120
126
 
121
- ai_service = container.ai_service()
122
- database = container.database()
127
+ ai_service = get_ai_service()
128
+ database = get_database()
123
129
  kwargs = await prepare_agent_call(
124
130
  node_id=ctx.node_id, node_type=self.type,
125
131
  parameters=params.model_dump(),
126
132
  context=ctx.raw, database=database,
127
133
  log_prefix="[AI Agent]",
128
134
  )
135
+ # ``execute_agent`` raises ``NodeUserError`` directly for typed
136
+ # openai SDK failures (context overflow, bad key, server down).
137
+ # Anything that comes back here as ``success=False`` is a real
138
+ # bug worth a stacktrace via ``RuntimeError``.
129
139
  response = await ai_service.execute_agent(ctx.node_id, **kwargs)
130
140
  if response.get("success"):
131
141
  return response.get("result") or response
@@ -28,6 +28,9 @@ class ChatAgentParams(BaseModel):
28
28
  provider: Literal[
29
29
  "openai", "anthropic", "gemini", "openrouter",
30
30
  "groq", "cerebras", "deepseek", "kimi", "mistral",
31
+ # Local-server providers — see ai_agent.Params for the proxy_url
32
+ # rationale. Same fix; same reason.
33
+ "ollama", "lmstudio",
31
34
  ] = "openai"
32
35
  model: str = Field(
33
36
  default="", json_schema_extra={"placeholder": "Select a model..."},
@@ -100,12 +103,12 @@ class ChatAgentNode(ActionNode):
100
103
  @Operation("execute", cost={"service": "chat_agent", "action": "run", "count": 1})
101
104
  async def execute_op(self, ctx: NodeContext, params: ChatAgentParams) -> Any:
102
105
  """Inlined from handlers/ai.handle_chat_agent (Wave 11.D.6)."""
103
- from core.container import container
106
+ from services.plugin.deps import get_ai_service, get_database
104
107
 
105
108
  from ._inline import prepare_agent_call
106
109
 
107
- ai_service = container.ai_service()
108
- database = container.database()
110
+ ai_service = get_ai_service()
111
+ database = get_database()
109
112
  kwargs = await prepare_agent_call(
110
113
  node_id=ctx.node_id, node_type=self.type,
111
114
  parameters=params.model_dump(),
@@ -1,48 +1,123 @@
1
- """Claude Code Agent — Wave 11.E.3 inlined.
1
+ """Claude Code Agent — multi-instance via `AICliService`.
2
2
 
3
- Shells out to the local ``claude`` CLI binary (isolated auth via
4
- ``services/claude_oauth.py``). Status updates broadcast on entry +
5
- exit; skill instructions concatenated into the system prompt.
3
+ Refactored from the single-task shim. ``Params.tasks`` is a list of
4
+ ``ClaudeTaskSpec``; each task gets its own git worktree, its own session
5
+ id, and runs in parallel under a 5-way semaphore.
6
+
7
+ Back-compat: an empty `tasks` array with a non-empty `prompt` falls back
8
+ to a single-task batch — preserves the legacy single-shot UX.
9
+
10
+ Skill instructions connected via the parent node's ``input-skill`` handle
11
+ are exposed to the CLI through the MCP server's ``listSkills`` /
12
+ ``getSkill`` tools (NOT concatenated into the system prompt — the
13
+ agent fetches them on-demand). Connected skill names ARE collected here
14
+ and passed to ``run_batch()`` so the MCP server can scope its responses.
6
15
  """
7
16
 
8
17
  from __future__ import annotations
9
18
 
10
19
  import time
20
+ import uuid
11
21
  from datetime import datetime
12
- from typing import Any, Optional
22
+ from pathlib import Path
23
+ from typing import Any, List, Optional
13
24
 
14
- from pydantic import ConfigDict, Field
25
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
15
26
 
16
27
  from core.logging import get_logger
17
- from services.plugin import ActionNode, NodeContext, Operation, TaskQueue
28
+ from services.plugin import ActionNode, NodeContext, NodeUserError, Operation, TaskQueue
18
29
  from services.plugin.edge_walker import collect_agent_connections
19
30
 
20
31
  from ._handles import STD_AGENT_HINTS, std_agent_handles
21
- from ._specialized import SpecializedAgentOutput, SpecializedAgentParams
22
32
 
23
33
  logger = get_logger(__name__)
24
34
 
25
35
 
26
- class ClaudeCodeAgentParams(SpecializedAgentParams):
27
- """Adds CLI-specific flags the handler body reads from payload."""
36
+ # Late import the cli_agent types so node_registry import doesn't pull
37
+ # the whole MCP / pool stack at module import time.
38
+ from services.cli_agent import ClaudeTaskSpec # noqa: E402
39
+ from services.cli_agent.types import SessionResultModel # noqa: E402
28
40
 
29
- max_turns: int = Field(default=10, ge=1)
30
- max_budget_usd: float = Field(default=5.0, ge=0.0)
31
- system_prompt: Optional[str] = Field(default=None)
32
- working_directory: Optional[str] = Field(default=None)
33
- allowed_tools: str = Field(
34
- default="Read,Edit,Bash,Glob,Grep,Write",
35
- )
41
+
42
+ class ClaudeCodeAgentParams(BaseModel):
43
+ """Multi-task batch parameters for Claude Code.
44
+
45
+ Two paths:
46
+ 1. ``tasks=[...]`` — explicit list, runs N in parallel
47
+ 2. ``tasks=[]`` + legacy ``prompt`` — synthesises a single-task batch
48
+ """
49
+
50
+ tasks: List[ClaudeTaskSpec] = Field(
51
+ default_factory=list,
52
+ description="List of Claude tasks to run in parallel (max 5 concurrent).",
53
+ json_schema_extra={"rows": 1},
54
+ )
55
+ # Legacy single-task fallback ----------------------------------------
56
+ prompt: str = Field(
57
+ default="",
58
+ description="Legacy: single-prompt fallback used only when "
59
+ "`tasks` is empty.",
60
+ json_schema_extra={"rows": 4, "placeholder": "Or use the tasks array..."},
61
+ )
62
+ model: str = Field(
63
+ default="claude-sonnet-4-6",
64
+ description="Default model for tasks that don't override it.",
65
+ )
66
+ system_prompt: Optional[str] = Field(default=None, json_schema_extra={"rows": 3})
67
+ working_directory: Optional[str] = Field(
68
+ default=None,
69
+ description="Git repo root. Defaults to the workflow's workspace dir.",
70
+ )
71
+ max_parallel: int = Field(
72
+ default=5, ge=1, le=20,
73
+ description="Concurrency cap.",
74
+ )
75
+ allowed_credentials: List[str] = Field(
76
+ default_factory=list,
77
+ description="Credential names the CLI is permitted to fetch via MCP.",
78
+ )
79
+
80
+ # Saved workflow JSON may persist these list fields as `null` rather
81
+ # than `[]` when the user has never edited them. Coerce so Pydantic's
82
+ # strict list validation doesn't reject the params on load.
83
+ @field_validator("tasks", "allowed_credentials", mode="before")
84
+ @classmethod
85
+ def _none_is_empty_list(cls, v: Any) -> Any:
86
+ return [] if v is None else v
36
87
 
37
88
  model_config = ConfigDict(extra="ignore")
38
89
 
39
90
 
91
+ class ClaudeCodeAgentOutput(BaseModel):
92
+ """Aggregated batch output."""
93
+ success: bool = True
94
+ n_tasks: int = 0
95
+ n_succeeded: int = 0
96
+ n_failed: int = 0
97
+ total_cost_usd: Optional[float] = None
98
+ wall_clock_ms: int = 0
99
+ tasks: List[SessionResultModel] = Field(default_factory=list)
100
+ provider: str = "claude"
101
+ timestamp: Optional[str] = None
102
+ # Legacy single-task fields, populated when n_tasks==1 for back-compat:
103
+ response: Optional[str] = None
104
+ session_id: Optional[str] = None
105
+ cost_usd: Optional[float] = None
106
+ # `extra="forbid"` so a malformed CLI envelope raises a real
107
+ # ValidationError at the Output boundary instead of silently emitting
108
+ # an unparseable shape downstream.
109
+ model_config = ConfigDict(extra="forbid")
110
+
111
+
40
112
  class ClaudeCodeAgentNode(ActionNode):
41
113
  type = "claude_code_agent"
42
114
  display_name = "Claude Code"
43
115
  subtitle = "Agentic Coding"
44
116
  group = ("agent",)
45
- description = "Claude Code CLI as a specialized agent"
117
+ description = (
118
+ "Run N parallel Claude Code CLI sessions over a list of tasks. "
119
+ "Each task is isolated in its own git worktree."
120
+ )
46
121
  component_kind = "agent"
47
122
  handles = std_agent_handles()
48
123
  ui_hints = STD_AGENT_HINTS
@@ -50,84 +125,221 @@ class ClaudeCodeAgentNode(ActionNode):
50
125
  task_queue = TaskQueue.AI_HEAVY
51
126
 
52
127
  Params = ClaudeCodeAgentParams
53
- Output = SpecializedAgentOutput
128
+ Output = ClaudeCodeAgentOutput
54
129
 
55
- @Operation("execute", cost={"service": "claude_code_agent", "action": "run", "count": 1})
56
- async def execute_op(self, ctx: NodeContext, params: ClaudeCodeAgentParams) -> Any:
57
- from core.container import container
58
- from services.claude_code_service import get_claude_code_service
130
+ @Operation(
131
+ "execute",
132
+ cost={"service": "claude_code_agent", "action": "run", "count": 1},
133
+ )
134
+ async def execute_op(
135
+ self, ctx: NodeContext, params: ClaudeCodeAgentParams,
136
+ ) -> Any:
137
+ from services.cli_agent.service import get_ai_cli_service
138
+ from services.cli_agent.types import session_result_to_model
139
+ from services.plugin.deps import get_database
59
140
  from services.status_broadcaster import get_status_broadcaster
60
141
 
61
142
  start_time = time.time()
62
143
  broadcaster = get_status_broadcaster()
63
144
  workflow_id = ctx.workflow_id
64
145
  node_id = ctx.node_id
65
- payload = params.model_dump()
66
146
 
67
147
  await broadcaster.update_node_status(
68
148
  node_id, "executing",
69
- {"message": "Starting Claude Code..."},
149
+ {"message": "Starting Claude Code batch..."},
70
150
  workflow_id=workflow_id,
71
151
  )
72
152
 
73
- # Resolve prompt: explicit param wins; otherwise pull text from
74
- # the upstream node connected to input-main.
75
- prompt = payload.get("prompt", "")
76
- if not prompt:
77
- for edge in ctx.raw.get("edges", []):
78
- if edge.get("target") == node_id and edge.get("targetHandle") in ("input-main", None):
79
- src = ctx.raw.get("outputs", {}).get(edge.get("source"), {})
80
- if isinstance(src, dict):
81
- prompt = src.get("message") or src.get("text") or src.get("content") or str(src)
82
- elif src:
83
- prompt = str(src)
84
- if prompt:
85
- break
86
- if not prompt:
87
- raise RuntimeError("No prompt provided")
88
-
89
- database = container.database()
90
- _, skill_data, _, _, _ = await collect_agent_connections(node_id, ctx.raw, database)
91
-
92
- system_parts = []
93
- if payload.get("system_prompt"):
94
- system_parts.append(payload["system_prompt"])
95
- for skill in skill_data:
96
- instr = skill.get("parameters", {}).get("instructions", "")
97
- if instr:
98
- system_parts.append(instr)
99
-
100
- model = payload.get("model", "claude-sonnet-4-6")
101
- await broadcaster.update_node_status(
102
- node_id, "executing",
103
- {"message": f"Running Claude Code ({model})..."},
104
- workflow_id=workflow_id,
153
+ # Collect connected memory/skills/tools/input/task in one pass
154
+ # same edge-walker the AI Agent uses (services/plugin/edge_walker.py).
155
+ # This must run BEFORE prompt resolution so the auto-fallback can
156
+ # read from `input_data` exactly the way `nodes/agent/_inline.py`
157
+ # does for the standard agent path.
158
+ database = get_database()
159
+ memory_data, skill_data, tool_data, input_data, _ = (
160
+ await collect_agent_connections(
161
+ node_id, ctx.raw, database, log_prefix="[Claude Code]",
162
+ )
105
163
  )
164
+ connected_skills = [
165
+ s.get("skill_name") or s.get("label")
166
+ for s in skill_data
167
+ if s.get("skill_name") or s.get("label")
168
+ ]
106
169
 
107
- service = get_claude_code_service()
108
- data = await service.execute(
109
- prompt=prompt,
170
+ # Memory bridge: claude maintains its own session JSONL on disk
171
+ # under `<CLAUDE_CONFIG_DIR>/projects/<cwd-encoded>/<UUID>.jsonl`.
172
+ # The project_key is derived from cwd (`[^a-zA-Z0-9.-] -> -`),
173
+ # so memory continuity needs (a) a STABLE cwd across runs and
174
+ # (b) the right session_id passed on argv.
175
+ #
176
+ # (a) is handled by AICliService passing memory_bound=True so
177
+ # AICliSession spawns under repo_root instead of an ephemeral
178
+ # worktree — see `services/cli_agent/session.py:cwd()`.
179
+ #
180
+ # (b) splits on whether claude has run for this memory before:
181
+ #
182
+ # - First run (no last_session_id): pass `--session-id <UUID5>`
183
+ # where UUID5 is deterministically derived from
184
+ # (memory_node_id, simpleMemory.session_id). Claude creates
185
+ # the session at exactly that UUID, writes its JSONL to a
186
+ # predictable location. _persist_memory saves the same
187
+ # UUID as last_session_id after success.
188
+ #
189
+ # - Subsequent runs: pass `--resume <last_session_id>`.
190
+ # Claude finds its own JSONL and continues.
191
+ #
192
+ # Stale-`last_session_id` self-healing: if `--resume` fails with
193
+ # "No conversation found", `_persist_memory` clears the stale
194
+ # UUID; the next run falls into the first-run branch and
195
+ # recovers automatically.
196
+ resume_session_id: Optional[str] = None
197
+ first_run_session_uuid: Optional[str] = None
198
+ if memory_data:
199
+ last = memory_data.get("last_session_id") or None
200
+ if last:
201
+ resume_session_id = last
202
+ else:
203
+ raw = memory_data.get("session_id") or memory_data["node_id"]
204
+ name = f"{memory_data['node_id']}:{raw}"
205
+ first_run_session_uuid = str(
206
+ uuid.uuid5(uuid.NAMESPACE_OID, name)
207
+ )
208
+ logger.info(
209
+ "[Claude Code memory] memory_node=%s last_session_id=%s "
210
+ "-> %s",
211
+ memory_data.get("node_id"),
212
+ last,
213
+ f"--resume {resume_session_id}" if resume_session_id
214
+ else f"--session-id {first_run_session_uuid} (first run)",
215
+ )
216
+
217
+ tasks = list(params.tasks)
218
+ if not tasks:
219
+ # AI-Agent-style prompt resolution: explicit `prompt` field
220
+ # wins; otherwise extract from upstream input via the same
221
+ # `message > text > content > str()` chain `_inline.py` uses.
222
+ prompt = (params.prompt or "").strip()
223
+ if not prompt and input_data:
224
+ prompt = (
225
+ (input_data.get("message") if isinstance(input_data, dict) else None)
226
+ or (input_data.get("text") if isinstance(input_data, dict) else None)
227
+ or (input_data.get("content") if isinstance(input_data, dict) else None)
228
+ or str(input_data)
229
+ )
230
+ if not prompt:
231
+ raise NodeUserError(
232
+ "Claude Code agent has no prompt — fill in the Prompt "
233
+ "field, populate the Tasks array, or connect a node "
234
+ "to the Input handle."
235
+ )
236
+ spec_kwargs: dict = {
237
+ "prompt": prompt,
238
+ "model": params.model,
239
+ "system_prompt": params.system_prompt,
240
+ }
241
+ if resume_session_id:
242
+ spec_kwargs["resume_session_id"] = resume_session_id
243
+ elif first_run_session_uuid:
244
+ spec_kwargs["session_id"] = first_run_session_uuid
245
+ tasks = [ClaudeTaskSpec(**spec_kwargs)]
246
+ else:
247
+ # Apply node-level defaults to tasks that don't override.
248
+ for i, t in enumerate(tasks):
249
+ changed: dict = {}
250
+ if not t.model and params.model:
251
+ changed["model"] = params.model
252
+ if not t.system_prompt and params.system_prompt:
253
+ changed["system_prompt"] = params.system_prompt
254
+ if not t.session_id and not t.resume_session_id:
255
+ if resume_session_id:
256
+ changed["resume_session_id"] = resume_session_id
257
+ elif first_run_session_uuid:
258
+ changed["session_id"] = first_run_session_uuid
259
+ if changed:
260
+ tasks[i] = t.model_copy(update=changed)
261
+
262
+ # Memory continuity requires serial execution; parallel
263
+ # `--resume` against one session JSONL would corrupt it.
264
+ if memory_data and len(tasks) > 1:
265
+ raise NodeUserError(
266
+ "Memory-bound batches must run one task at a time. "
267
+ "Reduce Tasks to a single entry, or disconnect the "
268
+ "memory node."
269
+ )
270
+
271
+ # Workspace dir — workflow.py injects this into context
272
+ workspace_dir = ctx.raw.get("workspace_dir") or params.working_directory
273
+ if workspace_dir is None:
274
+ from core.config import Settings
275
+ workspace_dir = Path(Settings().workspace_base_resolved) / (
276
+ workflow_id or "default"
277
+ )
278
+ workspace_dir = Path(workspace_dir)
279
+
280
+ repo_root = (
281
+ Path(params.working_directory) if params.working_directory else None
282
+ )
283
+
284
+ svc = get_ai_cli_service()
285
+ result = await svc.run_batch(
286
+ "claude",
287
+ tasks=tasks,
110
288
  node_id=node_id,
111
- model=model,
112
- cwd=payload.get("working_directory") or None,
113
- allowed_tools=payload.get("allowed_tools", "Read,Edit,Bash,Glob,Grep,Write"),
114
- max_turns=int(payload.get("max_turns", 10)),
115
- max_budget_usd=float(payload.get("max_budget_usd", 5.0)),
116
- system_prompt="\n\n".join(system_parts) if system_parts else None,
289
+ workflow_id=workflow_id or "",
290
+ workspace_dir=workspace_dir,
291
+ broadcaster=broadcaster,
292
+ repo_root=repo_root,
293
+ connected_skill_names=connected_skills,
294
+ connected_tools=tool_data,
295
+ connected_memory=memory_data,
296
+ allowed_credentials=params.allowed_credentials,
297
+ max_parallel=params.max_parallel,
298
+ )
299
+
300
+ elapsed = time.time() - start_time
301
+ logger.debug(
302
+ "[claude_code_agent] node=%s tasks=%d ok=%d fail=%d elapsed=%.2fs",
303
+ node_id, result.n_tasks, result.n_succeeded, result.n_failed, elapsed,
117
304
  )
118
305
 
119
306
  await broadcaster.update_node_status(
120
- node_id, "success",
121
- {"message": "Claude Code completed"},
307
+ node_id, "success" if result.n_failed == 0 else "warning",
308
+ {
309
+ "message": (
310
+ f"Batch complete: {result.n_succeeded}/{result.n_tasks} "
311
+ f"succeeded"
312
+ ),
313
+ "n_tasks": result.n_tasks,
314
+ "n_succeeded": result.n_succeeded,
315
+ "n_failed": result.n_failed,
316
+ },
122
317
  workflow_id=workflow_id,
123
318
  )
124
- logger.debug("[ClaudeCode] node=%s elapsed=%.2fs", node_id, time.time() - start_time)
319
+
320
+ task_models = [session_result_to_model(t).model_dump() for t in result.tasks]
321
+
322
+ # Legacy single-task convenience fields
323
+ legacy_response = None
324
+ legacy_session_id = None
325
+ legacy_cost = None
326
+ if len(result.tasks) == 1:
327
+ legacy_response = result.tasks[0].response
328
+ legacy_session_id = result.tasks[0].session_id
329
+ legacy_cost = result.tasks[0].cost_usd
125
330
 
126
331
  return {
127
- "response": data.get("result", ""),
128
- "model": model,
129
- "provider": "anthropic",
130
- "session_id": data.get("session_id", ""),
131
- "usage": data.get("usage", {}),
132
- "timestamp": datetime.now().isoformat(),
332
+ "success": result.n_failed == 0,
333
+ "n_tasks": result.n_tasks,
334
+ "n_succeeded": result.n_succeeded,
335
+ "n_failed": result.n_failed,
336
+ "total_cost_usd": result.total_cost_usd,
337
+ "wall_clock_ms": result.wall_clock_ms,
338
+ "tasks": task_models,
339
+ "provider": result.provider,
340
+ "timestamp": result.timestamp or datetime.now().isoformat(),
341
+ "response": legacy_response,
342
+ "session_id": legacy_session_id,
343
+ "cost_usd": legacy_cost,
133
344
  }
345
+