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
@@ -63,6 +63,22 @@ class _EmptyOutput(BaseModel):
63
63
  pass
64
64
 
65
65
 
66
+ # Group memberships that mark a node as auxiliary configuration —
67
+ # its panel inherits the parent's main inputs instead of showing
68
+ # direct inputs. Centralized here so the frontend doesn't need to
69
+ # know any group strings.
70
+ _CONFIG_NODE_GROUPS = frozenset({"memory", "tool"})
71
+
72
+
73
+ def _derive_auto_ui_hints(group: Sequence[str]) -> Dict[str, Any]:
74
+ """Auto-derived uiHints based on group membership. Plugin-declared
75
+ ``ui_hints`` override these — explicit always wins."""
76
+ hints: Dict[str, Any] = {}
77
+ if any(g in _CONFIG_NODE_GROUPS for g in group):
78
+ hints["isConfigNode"] = True
79
+ return hints
80
+
81
+
66
82
  class BaseNode:
67
83
  """Abstract plugin node. Do not instantiate directly — subclass
68
84
  :class:`ActionNode`, :class:`TriggerNode`, or :class:`ToolNode`.
@@ -111,6 +127,7 @@ class BaseNode:
111
127
  handles: ClassVar[Sequence[Dict[str, Any]]] = ()
112
128
  visibility: ClassVar[str] = "all"
113
129
  hide_output_handle: ClassVar[bool] = False
130
+ hide_input_handle: ClassVar[bool] = False
114
131
  ui_hints: ClassVar[Dict[str, Any]] = {}
115
132
  annotations: ClassVar[Dict[str, Any]] = {}
116
133
 
@@ -140,6 +157,27 @@ class BaseNode:
140
157
  cls._operations = collect_operations(cls)
141
158
  if abstract or not cls.type:
142
159
  return
160
+ # Auto-hide default canvas input/output handles for nodes whose
161
+ # primary surface area is the LLM-tool call path. Two cases:
162
+ # * Pure ToolNodes (component_kind="tool" -- calculator,
163
+ # duckduckgoSearch, writeTodos, agentBuilder, ...). They
164
+ # wire through their own output-tool handle; the hardcoded
165
+ # SquareNode input-main + output-main are visual clutter.
166
+ # * Dual-purpose ActionNodes with usable_as_tool=True (gmail,
167
+ # twitter*, brave_search, all 16 android nodes via cascade,
168
+ # code executors via cascade, ...). Same reasoning: the
169
+ # LLM-tool path is the dominant use; default handles confuse
170
+ # the canvas.
171
+ # Subclasses opt out by explicitly setting either flag to False
172
+ # on the class.
173
+ is_tool_oriented = (
174
+ cls.usable_as_tool or cls.component_kind == "tool"
175
+ )
176
+ if is_tool_oriented:
177
+ if "hide_input_handle" not in cls.__dict__:
178
+ cls.hide_input_handle = True
179
+ if "hide_output_handle" not in cls.__dict__:
180
+ cls.hide_output_handle = True
143
181
  # Eager registry write — same four registries as @register_node.
144
182
  from services.node_registry import register_node, register_node_class
145
183
  register_node(
@@ -185,10 +223,14 @@ class BaseNode:
185
223
  meta["credentials"] = [c.id for c in cls.credentials]
186
224
  if cls.hide_output_handle:
187
225
  meta["hideOutputHandle"] = True
226
+ if cls.hide_input_handle:
227
+ meta["hideInputHandle"] = True
188
228
  if cls.visibility != "all":
189
229
  meta["visibility"] = cls.visibility
190
- if cls.ui_hints:
191
- meta["uiHints"] = dict(cls.ui_hints)
230
+ ui_hints = _derive_auto_ui_hints(cls.group)
231
+ ui_hints.update(cls.ui_hints)
232
+ if ui_hints:
233
+ meta["uiHints"] = ui_hints
192
234
  return meta
193
235
 
194
236
  # ---- legacy handler adapter ------------------------------------------
@@ -13,16 +13,134 @@ Two concrete bases:
13
13
 
14
14
  Both are discovered at import time via :data:`CREDENTIAL_REGISTRY`
15
15
  (populated by ``__init_subclass__``). Nothing else needs to wire them.
16
+
17
+ Validation is also a base-class concern. Every "validate this key"
18
+ flow shares the same wiring (read api_key + session_id from request,
19
+ call provider probe, store on success, broadcast status, return
20
+ envelope). Only the probe itself varies. That common scaffold lives on
21
+ :meth:`Credential.validate`; subclasses override the lighter
22
+ :meth:`Credential._probe` to supply the per-provider call. See
23
+ :class:`ProbeResult` for the typed return shape.
16
24
  """
17
25
 
18
26
  from __future__ import annotations
19
27
 
20
- from typing import Any, ClassVar, Dict, Literal, Optional, Sequence
28
+ import logging
29
+ import time
30
+ from dataclasses import dataclass, field
31
+ from typing import Any, ClassVar, Dict, List, Literal, Optional, Sequence
32
+
33
+ import httpx
34
+
35
+ logger = logging.getLogger(__name__)
21
36
 
22
37
 
23
38
  CREDENTIAL_REGISTRY: Dict[str, type] = {}
24
39
 
25
40
 
41
+ @dataclass
42
+ class ProbeResult:
43
+ """Outcome of a per-provider validation probe.
44
+
45
+ Carries the common ``valid`` / ``message`` flags every validator
46
+ returns, plus optional fields a few providers extend with
47
+ (``models`` for LLM ``/v1/models`` lists, ``model_params`` for
48
+ Ollama / LM Studio per-model context, ``extra`` for
49
+ provider-specific extensions like Apify's ``username`` / ``email``
50
+ / ``plan``). The base ``Credential.validate`` reads these fields
51
+ when wiring storage / broadcast / response envelope so subclasses
52
+ don't repeat any of that scaffolding.
53
+ """
54
+
55
+ valid: bool
56
+ message: str = ""
57
+ models: List[str] = field(default_factory=list)
58
+ model_params: Optional[Dict[str, Dict[str, Any]]] = None
59
+ # Free-form passthrough fields for provider-specific extensions
60
+ # (Apify returns username / email / plan; Twitter returns user id;
61
+ # Maps returns nothing). Merged into the response envelope.
62
+ extra: Dict[str, Any] = field(default_factory=dict)
63
+
64
+
65
+ def classify_credential_error(
66
+ exc: BaseException, *, display_name: str
67
+ ) -> ProbeResult:
68
+ """Map a transport / SDK exception to a typed ``ProbeResult``.
69
+
70
+ Single source of truth for credential-error → user-message mapping.
71
+ Used by the base ``Credential.validate`` after a ``_probe`` call
72
+ raises, and by the local-LLM probe directly. Catches the documented
73
+ ``httpx`` / ``openai`` exception hierarchy so operator logs see
74
+ "HTTP 401" / "connect-refused" / "timeout" instead of an opaque
75
+ repr.
76
+
77
+ Returns a ``ProbeResult(valid=False, message=...)`` carrying the
78
+ user-facing string. Operator logs are emitted at WARN by the
79
+ caller — this helper just classifies, doesn't log.
80
+ """
81
+ import openai # local import: openai is a heavy SDK
82
+
83
+ if isinstance(exc, httpx.TimeoutException) or isinstance(exc, openai.APITimeoutError):
84
+ return ProbeResult(
85
+ valid=False,
86
+ message=f"Request to {display_name} timed out — try again or check the network.",
87
+ )
88
+
89
+ if isinstance(exc, httpx.ConnectError) or isinstance(exc, openai.APIConnectionError):
90
+ return ProbeResult(
91
+ valid=False,
92
+ message=f"Could not reach {display_name}. Is the server running?",
93
+ )
94
+
95
+ if isinstance(exc, httpx.HTTPStatusError):
96
+ status = exc.response.status_code
97
+ return _classify_status(status, display_name)
98
+
99
+ if isinstance(exc, openai.AuthenticationError):
100
+ return _classify_status(401, display_name)
101
+ if isinstance(exc, openai.PermissionDeniedError):
102
+ return _classify_status(403, display_name)
103
+ if isinstance(exc, openai.NotFoundError):
104
+ return _classify_status(404, display_name)
105
+ if isinstance(exc, openai.RateLimitError):
106
+ return _classify_status(429, display_name)
107
+ if isinstance(exc, openai.APIStatusError):
108
+ status = getattr(getattr(exc, "response", None), "status_code", 500)
109
+ return _classify_status(status, display_name)
110
+
111
+ # Unknown exception: surface the type name so the operator log line
112
+ # is still useful, but don't dump a stacktrace into the user's toast.
113
+ return ProbeResult(
114
+ valid=False,
115
+ message=f"Could not validate {display_name}: {type(exc).__name__}: {exc}",
116
+ )
117
+
118
+
119
+ def _classify_status(status: int, display_name: str) -> ProbeResult:
120
+ """Map an HTTP status code to a user-facing message."""
121
+ if status in (401, 403):
122
+ return ProbeResult(valid=False, message=f"{display_name} rejected the API key.")
123
+ if status == 404:
124
+ return ProbeResult(
125
+ valid=False,
126
+ message=f"{display_name} returned 404 — endpoint not found. Check the URL.",
127
+ )
128
+ if status == 429:
129
+ return ProbeResult(
130
+ valid=False,
131
+ message=f"{display_name} rate-limited the request — try again shortly.",
132
+ )
133
+ if 500 <= status < 600:
134
+ return ProbeResult(
135
+ valid=False,
136
+ message=f"{display_name} returned HTTP {status} — try again later.",
137
+ )
138
+ return ProbeResult(
139
+ valid=False,
140
+ message=f"{display_name} returned HTTP {status}.",
141
+ )
142
+
143
+
26
144
  class Credential:
27
145
  """Base class for provider credentials.
28
146
 
@@ -73,6 +191,105 @@ class Credential:
73
191
  """
74
192
  return request
75
193
 
194
+ # ---- Validation -------------------------------------------------
195
+ #
196
+ # Every "validate this credential" flow follows the same wiring:
197
+ # read api_key + session_id from the request, run a per-provider
198
+ # probe, store on success, broadcast status, return the standard
199
+ # response envelope. Only the probe call genuinely varies — so the
200
+ # scaffolding lives on the base and subclasses override the lighter
201
+ # :meth:`_probe` hook. The dispatch entry point (called by the WS
202
+ # router) is :meth:`validate`; subclasses with non-standard
203
+ # side-effect ordering (e.g. local-LLM credentials store under TWO
204
+ # keys) override :meth:`validate` directly.
205
+
206
+ @classmethod
207
+ async def validate(cls, data: Dict[str, Any]) -> Dict[str, Any]:
208
+ """Run the credential validation probe and wire side effects.
209
+
210
+ The shared scaffold:
211
+ 1. Read ``api_key`` + ``session_id`` from the request data.
212
+ 2. Guard: missing key → fast-fail envelope.
213
+ 3. Call :meth:`_probe` (subclass-supplied; provider-specific).
214
+ 4. On exception, classify via :func:`classify_credential_error`.
215
+ 5. On valid result, persist via ``auth_service.store_api_key``.
216
+ 6. Broadcast the new status via
217
+ ``StatusBroadcaster.update_api_key_status`` (always — invalid
218
+ results clear stale state on connected clients).
219
+ 7. Return the standard response envelope.
220
+
221
+ Subclasses with non-standard storage / broadcast (e.g. local-LLM
222
+ credentials that store both URL and placeholder key) override
223
+ this method directly.
224
+ """
225
+ from core.container import container
226
+ from services.status_broadcaster import get_status_broadcaster
227
+
228
+ api_key = (data.get("api_key") or "").strip()
229
+ session_id = data.get("session_id", "default")
230
+ if not api_key:
231
+ return {
232
+ "success": False,
233
+ "valid": False,
234
+ "error": f"{cls.id} api_key required",
235
+ }
236
+
237
+ try:
238
+ result = await cls._probe(api_key)
239
+ except Exception as exc: # noqa: BLE001 — classified below
240
+ display = cls.display_name or cls.id
241
+ result = classify_credential_error(exc, display_name=display)
242
+ logger.warning(
243
+ "[%s] credential probe failed: %s",
244
+ cls.id,
245
+ result.message,
246
+ )
247
+
248
+ broadcaster = get_status_broadcaster()
249
+ auth_service = container.auth_service()
250
+
251
+ if result.valid:
252
+ await auth_service.store_api_key(
253
+ provider=cls.id,
254
+ api_key=api_key,
255
+ models=result.models,
256
+ session_id=session_id,
257
+ model_params=result.model_params,
258
+ )
259
+
260
+ await broadcaster.update_api_key_status(
261
+ provider=cls.id,
262
+ valid=result.valid,
263
+ message=result.message,
264
+ has_key=result.valid,
265
+ models=result.models,
266
+ )
267
+
268
+ return {
269
+ "success": True,
270
+ "provider": cls.id,
271
+ "valid": result.valid,
272
+ "message": result.message,
273
+ "models": result.models,
274
+ "timestamp": time.time(),
275
+ **result.extra,
276
+ }
277
+
278
+ @classmethod
279
+ async def _probe(cls, api_key: str) -> ProbeResult:
280
+ """Run the per-provider validation probe.
281
+
282
+ Subclass override point — the probe MUST NOT do storage,
283
+ broadcasts, or registry mutation. It returns a
284
+ :class:`ProbeResult` describing what the upstream returned;
285
+ :meth:`validate` does the rest. Raise ``httpx.*`` /
286
+ ``openai.OpenAIError`` to let the base map them to a typed
287
+ message via :func:`classify_credential_error`.
288
+ """
289
+ raise NotImplementedError(
290
+ f"Credential subclass {cls.__name__} must override _probe()"
291
+ )
292
+
76
293
 
77
294
  class OAuth2Credential(Credential):
78
295
  """OAuth 2.0 with refresh-token support.
@@ -130,6 +347,15 @@ class ApiKeyCredential(Credential):
130
347
  category = "Search"
131
348
  key_name = "X-Subscription-Token"
132
349
  key_location = "header"
350
+ probe_url = "https://api.search.brave.com/res/v1/web/search"
351
+ probe_params = {"q": "ping", "count": 1}
352
+
353
+ Subclasses get a default :meth:`_probe` for free as long as they
354
+ declare a ``probe_url``. Auth attaches via :meth:`inject` (header /
355
+ query / bearer). Subclasses with bespoke validation (URL with
356
+ embedded token, SDK-based probe, custom 200-with-error envelope)
357
+ override :meth:`_probe` directly — see ``TelegramCredential``,
358
+ ``GoogleMapsCredential``, ``ApifyCredential``.
133
359
  """
134
360
 
135
361
  auth: ClassVar[Literal["api_key"]] = "api_key"
@@ -139,6 +365,20 @@ class ApiKeyCredential(Credential):
139
365
  # Extra fields stored alongside (e.g. "apify_account_id" for Apify)
140
366
  extra_fields: ClassVar[Sequence[str]] = ()
141
367
 
368
+ # ---- Declarative HTTP probe ------------------------------------
369
+ #
370
+ # The default :meth:`_probe` builds a request from these attributes,
371
+ # runs it through :meth:`inject` to attach the key, sends via httpx,
372
+ # and calls :meth:`_handle_probe_response` on a 2xx response.
373
+ # ``httpx.HTTPStatusError`` / ``TimeoutException`` / ``ConnectError``
374
+ # propagate to :meth:`Credential.validate`, where
375
+ # :func:`classify_credential_error` produces the user-facing message.
376
+ probe_url: ClassVar[str] = "" # set to enable the default probe
377
+ probe_method: ClassVar[str] = "GET"
378
+ probe_params: ClassVar[Dict[str, Any]] = {}
379
+ probe_json: ClassVar[Optional[Dict[str, Any]]] = None
380
+ probe_timeout_seconds: ClassVar[float] = 10.0
381
+
142
382
  @classmethod
143
383
  async def resolve(cls, *, user_id: str = "owner") -> Dict[str, Any]:
144
384
  from core.container import container
@@ -173,3 +413,50 @@ class ApiKeyCredential(Credential):
173
413
  qs[name] = api_key
174
414
  request = {**request, "params": qs}
175
415
  return request
416
+
417
+ @classmethod
418
+ async def _probe(cls, api_key: str) -> ProbeResult:
419
+ """Default declarative HTTP probe.
420
+
421
+ Subclasses set ``probe_url`` (and optionally ``probe_method``,
422
+ ``probe_params``, ``probe_json``); auth attaches via
423
+ :meth:`inject`. Override :meth:`_handle_probe_response` to
424
+ extract provider-specific metadata or detect API-level failures
425
+ embedded in a 2xx response (e.g. Telegram's ``{ok: false}``
426
+ envelope, Maps' ``status: REQUEST_DENIED``).
427
+ """
428
+ if not cls.probe_url:
429
+ raise NotImplementedError(
430
+ f"Credential subclass {cls.__name__} must override _probe() "
431
+ f"or set the `probe_url` class attribute"
432
+ )
433
+
434
+ request = cls.inject(
435
+ {"api_key": api_key},
436
+ {"headers": {}, "params": dict(cls.probe_params)},
437
+ )
438
+
439
+ async with httpx.AsyncClient(timeout=cls.probe_timeout_seconds) as client:
440
+ response = await client.request(
441
+ cls.probe_method,
442
+ cls.probe_url,
443
+ headers=request.get("headers") or None,
444
+ params=request.get("params") or None,
445
+ json=cls.probe_json,
446
+ )
447
+ response.raise_for_status()
448
+ return cls._handle_probe_response(response)
449
+
450
+ @classmethod
451
+ def _handle_probe_response(cls, response: httpx.Response) -> ProbeResult:
452
+ """Translate a 2xx probe response to a :class:`ProbeResult`.
453
+
454
+ Default: success message only. Override to inspect the body —
455
+ for example to surface bot identity (Telegram ``getMe``) or to
456
+ catch wrapped failures that arrive as 200s (Telegram
457
+ ``{ok: false}``).
458
+ """
459
+ return ProbeResult(
460
+ valid=True,
461
+ message=f"{cls.display_name or cls.id} API key is valid",
462
+ )
@@ -0,0 +1,105 @@
1
+ """Lazy dependency-injection helpers (Wave 11.I, milestone T-residual).
2
+
3
+ Plugin packages need lazy imports of singletons from
4
+ :mod:`core.container` to avoid the
5
+ ``nodes/<plugin>/__init__ -> core.container -> nodes.android._dispatcher
6
+ -> nodes.android.__init__ -> _router`` import cycle that bites at
7
+ package load time. Pre-T-residual the codebase had 53 inline
8
+ ``from core.container import container`` statements scattered across
9
+ plugin handlers + services. These helpers consolidate the pattern
10
+ into one place.
11
+
12
+ **Important: NOT memoised.** Both helpers re-resolve the singleton on
13
+ every call. Test fixtures swap the auth-service / database instances
14
+ mid-test via the dependency-injection container's ``override``
15
+ mechanism; a memoised cache would lock in the originally-resolved
16
+ instance and the override would be silently ignored. The lookup is
17
+ a dict access -- caching adds nothing measurable.
18
+
19
+ Usage::
20
+
21
+ from services.plugin.deps import get_auth_service
22
+
23
+ async def handle_thing(data, websocket):
24
+ auth_service = get_auth_service()
25
+ client_id = await auth_service.get_api_key("twitter_client_id")
26
+ ...
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from typing import TYPE_CHECKING, Any
32
+
33
+ if TYPE_CHECKING:
34
+ from core.cache import CacheService
35
+ from core.database import Database
36
+ from services.ai import AIService
37
+ from services.auth import AuthService
38
+
39
+
40
+ def get_auth_service() -> "AuthService":
41
+ """Resolve the singleton :class:`services.auth.AuthService`.
42
+
43
+ Lazy import + call-time lookup. Test monkeypatching depends on
44
+ the container override being read at call time, not at module
45
+ import time -- do NOT memoise.
46
+ """
47
+ from core.container import container
48
+
49
+ return container.auth_service()
50
+
51
+
52
+ def get_database() -> "Database":
53
+ """Resolve the singleton :class:`core.database.Database`.
54
+
55
+ Same NOT-memoised contract as :func:`get_auth_service`.
56
+ """
57
+ from core.container import container
58
+
59
+ return container.database()
60
+
61
+
62
+ def get_cache() -> "CacheService":
63
+ """Resolve the singleton :class:`core.cache.CacheService`."""
64
+ from core.container import container
65
+
66
+ return container.cache()
67
+
68
+
69
+ def get_ai_service() -> "AIService":
70
+ """Resolve the singleton :class:`services.ai.AIService`."""
71
+ from core.container import container
72
+
73
+ return container.ai_service()
74
+
75
+
76
+ def get_text_service() -> Any:
77
+ """Resolve the singleton ``TextService`` (text generation)."""
78
+ from core.container import container
79
+
80
+ return container.text_service()
81
+
82
+
83
+ def get_maps_service() -> Any:
84
+ """Resolve the singleton :class:`nodes.location._service.MapsService`."""
85
+ from core.container import container
86
+
87
+ return container.maps_service()
88
+
89
+
90
+ def get_android_service() -> Any:
91
+ """Resolve the singleton :class:`nodes.android._dispatcher.AndroidService`."""
92
+ from core.container import container
93
+
94
+ return container.android_service()
95
+
96
+
97
+ __all__ = [
98
+ "get_auth_service",
99
+ "get_database",
100
+ "get_cache",
101
+ "get_ai_service",
102
+ "get_text_service",
103
+ "get_maps_service",
104
+ "get_android_service",
105
+ ]
@@ -168,11 +168,19 @@ async def _build_memory_entry(
168
168
  ),
169
169
  "long_term_enabled": memory_params.get("long_term_enabled", False),
170
170
  "retrieval_count": int(memory_params.get("retrieval_count", 3)),
171
+ # Claude Code CLI session continuity: the UUID claude returned on
172
+ # the previous successful run. claude_code_agent passes this as
173
+ # `--resume <UUID>` so claude finds its own JSONL transcript at
174
+ # `<CLAUDE_CONFIG_DIR>/projects/<cwd-encoded>/<UUID>.jsonl`.
175
+ "last_session_id": memory_params.get("last_session_id"),
171
176
  }
172
- logger.debug(
173
- f"{log_prefix} Connected memory node: session={session_id} "
174
- f"(auto={not configured_session or configured_session == 'default'}), "
175
- f"content_length={len(entry['memory_content'])}"
177
+ logger.info(
178
+ "%s Connected memory node: node=%s session=%s (auto=%s) "
179
+ "content_length=%d last_session_id=%s",
180
+ log_prefix, memory_node_id, session_id,
181
+ not configured_session or configured_session == "default",
182
+ len(entry["memory_content"]),
183
+ entry["last_session_id"],
176
184
  )
177
185
  return entry
178
186