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,425 @@
1
+ """Pytest invariants for the status-broadcast contract (Wave 11.I, milestone U).
2
+
3
+ Mirrors :mod:`tests.credentials.test_credential_broadcasts` for the
4
+ plugin-status / refresh / send_custom_event surface. Locks the
5
+ post-Wave-11.I contract:
6
+
7
+ * The credential-mutation path uses :class:`WorkflowEvent`
8
+ (CloudEvents v1.0 envelope) -- already enforced by
9
+ :mod:`tests.credentials.test_credential_broadcasts`. Re-asserted
10
+ here for completeness.
11
+ * :func:`services.event_waiter.dispatch` /
12
+ :func:`dispatch_async` accept either a ``WorkflowEvent`` or a
13
+ ``(str, dict)`` pair via :func:`_unpack_event` (post-Q signature).
14
+ * ``send_custom_event`` callsites: enforced as a ratchet -- the
15
+ ``_LEGACY_RAW_DICT_CALLSITES`` allowlist enumerates the existing
16
+ raw-dict callers with documented WHY. New callsites must use
17
+ ``WorkflowEvent`` unless they're added to the allowlist with a
18
+ rationale.
19
+
20
+ Two carve-outs:
21
+
22
+ * ``_TELEMETRY_CARVE_OUT`` -- permanent exempts. High-frequency
23
+ / paired-wire / log streams that won't ever move to typed
24
+ envelopes (per-node-status broadcasts fire hundreds of times per
25
+ workflow run; envelope wrapping is pure overhead there).
26
+ * ``_LEGACY_RAW_DICT_BROADCASTS`` / ``_LEGACY_RAW_DICT_CALLSITES`` --
27
+ grandfathered until per-plugin migration. Each entry documents
28
+ WHY it's still raw and what would unlock it.
29
+
30
+ Companion: :mod:`tests.credentials.test_credential_broadcasts` (the
31
+ older sibling that locks the credential-broadcast contract via the
32
+ same ``inspect.getsource`` introspection style).
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import inspect
38
+ import re
39
+ from typing import FrozenSet
40
+
41
+ import pytest
42
+
43
+
44
+ # ============================================================================
45
+ # CARVE-OUTS
46
+ # ============================================================================
47
+ #
48
+ # Telemetry: permanent exempts. These methods broadcast on every
49
+ # user-action / log-line / lifecycle-tick. Wrapping each emission in a
50
+ # CloudEvents envelope adds bytes + JSON encoding overhead at sites that
51
+ # fire hundreds of times per workflow run -- not worth it. The wire
52
+ # frames are paired with their dual (e.g. ``api_key_status`` is paired
53
+ # with ``credential_catalogue_updated`` in the credential mutation
54
+ # path) so frontend listeners always have a typed channel available
55
+ # for state changes.
56
+ _TELEMETRY_CARVE_OUT: FrozenSet[str] = frozenset({
57
+ "update_api_key_status", # paired-wire with credential_catalogue_updated
58
+ "update_node_status", # ~hundreds per workflow run
59
+ "update_node_output", # paired with update_node_status
60
+ "update_variable", # per-write
61
+ "update_variables", # batch per-write
62
+ "update_workflow_status", # lifecycle ticks
63
+ "update_deployment_status", # lifecycle ticks
64
+ "broadcast_console_log", # log stream
65
+ "broadcast_terminal_log", # log stream
66
+ # Pure dispatcher -- iterates plugin-registered callbacks via
67
+ # TaskGroup, does not emit any broadcast itself. Per-plugin
68
+ # refresh callbacks live in nodes/<plugin>/_refresh.py and are
69
+ # subject to their own typed-envelope migration tracked outside
70
+ # the StatusBroadcaster class.
71
+ "_refresh_all_services",
72
+ })
73
+
74
+ # Plugin status updates that still emit raw ``{type: 'X_status', data:
75
+ # {...}}`` frames. The frontend's per-plugin status panels listen for
76
+ # these wire-frames directly today; switching to a CloudEvents-typed
77
+ # envelope is a frontend change too. Migration is per-plugin, not
78
+ # included in milestone U scope.
79
+ _LEGACY_RAW_DICT_BROADCASTS: FrozenSet[str] = frozenset({
80
+ "update_android_status", # FE: AndroidStatusPanel reads `android_status` wire-frame
81
+ "update_whatsapp_status", # FE: WhatsAppStatusPanel reads `whatsapp_status` wire-frame
82
+ "update_telegram_status", # FE: TelegramStatusPanel reads `telegram_status` wire-frame
83
+ })
84
+
85
+ # ``send_custom_event`` callers that still pass raw dicts. Each entry
86
+ # documents WHY. Per-plugin migration unlocks each one.
87
+ _LEGACY_RAW_DICT_CALLSITES: FrozenSet[str] = frozenset({
88
+ # WhatsApp event router emits 7 distinct wire types. Migration would
89
+ # also rename ``whatsapp_message_received`` etc. to
90
+ # ``whatsapp.message.received`` namespaced types, requiring FE
91
+ # listener updates.
92
+ "nodes/whatsapp/_service.py",
93
+ # Webhook router emits ``webhook_received``. Webhook payload is
94
+ # arbitrary JSON; the consumer (webhookTrigger node) shape is
95
+ # already abstract, so a typed envelope adds little value.
96
+ "routers/webhook.py",
97
+ # Task-delegation completion. Migration would namespace as
98
+ # ``agent.task.{succeeded,failed}``; matched by taskTrigger.
99
+ "services/handlers/tools.py",
100
+ # Agent Builder canvas-mutation broadcast. Emits
101
+ # ``workflow_ops_apply`` carrying a flat
102
+ # ``{workflow_id, caller_node_id, operations}`` shape consumed by
103
+ # ``client/src/hooks/useWorkflowOpsListener.ts``. Migration to
104
+ # WorkflowEvent would change the wire format (envelope vs flat),
105
+ # requiring a parallel rewrite of the frontend listener +
106
+ # invariant test fixtures. Deferred — typed-envelope migration
107
+ # tracked alongside the credentials.* / agent.task.* migrations.
108
+ "nodes/tool/agent_builder.py",
109
+ })
110
+
111
+
112
+ # ============================================================================
113
+ # REGEXES
114
+ # ============================================================================
115
+
116
+ # Inside a ``_refresh_*`` / ``update_*_status`` method body, the
117
+ # ``broadcast({...})`` call MUST consume a WorkflowEvent (looking for
118
+ # ``WorkflowEvent`` token in a 200-char window before the ``broadcast(``
119
+ # call) OR call ``broadcast_credential_event`` (which wraps WorkflowEvent
120
+ # internally).
121
+ _REFRESH_OR_STATUS_NAME = re.compile(r"^_refresh_|^update_\w+_status$")
122
+
123
+ # Function-defining line in source. Captures method name.
124
+ _METHOD_DEF = re.compile(r"^\s+(?:async )?def (\w+)\b")
125
+
126
+
127
+ # ============================================================================
128
+ # Helpers
129
+ # ============================================================================
130
+
131
+
132
+ def _is_typed_broadcast(method_source: str) -> bool:
133
+ """Heuristic: method source either references ``WorkflowEvent``
134
+ OR calls a typed-broadcast helper (``broadcast_credential_event``).
135
+ """
136
+ if "WorkflowEvent" in method_source:
137
+ return True
138
+ if "broadcast_credential_event" in method_source:
139
+ return True
140
+ return False
141
+
142
+
143
+ def _split_methods(class_source: str) -> dict[str, str]:
144
+ """Split a class body's source into ``{method_name: method_source}``.
145
+
146
+ Anchors on ``\\n (?:async )?def `` which marks method starts at
147
+ class-level indentation. Module-level functions are ignored.
148
+ """
149
+ methods: dict[str, str] = {}
150
+ parts = re.split(r"(\n (?:async )?def \w+)", class_source)
151
+ # Re-stitch: parts looks like [preamble, " def foo", body0, " def bar", body1, ...]
152
+ for i in range(1, len(parts), 2):
153
+ header = parts[i]
154
+ body = parts[i + 1] if i + 1 < len(parts) else ""
155
+ match = _METHOD_DEF.match(header.lstrip("\n"))
156
+ if match:
157
+ methods[match.group(1)] = header + body
158
+ return methods
159
+
160
+
161
+ # ============================================================================
162
+ # 1) Credential event uses WorkflowEvent (already locked elsewhere; re-pinned)
163
+ # ============================================================================
164
+
165
+
166
+ class TestCredentialBroadcastUsesWorkflowEvent:
167
+ """``broadcast_credential_event`` is the canonical typed-builder
168
+ helper. Pinned here too so a regression in either invariant file
169
+ catches the break.
170
+ """
171
+
172
+ def test_uses_workflow_event(self):
173
+ from services.status_broadcaster import StatusBroadcaster
174
+
175
+ src = inspect.getsource(StatusBroadcaster.broadcast_credential_event)
176
+ assert "WorkflowEvent" in src, (
177
+ "broadcast_credential_event must wrap WorkflowEvent (CloudEvents "
178
+ "v1.0). Removing the typed envelope breaks the cross-tab "
179
+ "catalogue refresh contract."
180
+ )
181
+
182
+
183
+ # ============================================================================
184
+ # 2) event_waiter.dispatch accepts WorkflowEvent (post-Q signature)
185
+ # ============================================================================
186
+
187
+
188
+ class TestEventWaiterDispatchAcceptsEnvelope:
189
+ """Wave 11.I, milestone Q. ``dispatch`` and ``dispatch_async``
190
+ accept either a ``WorkflowEvent`` or a ``(str, dict)`` pair via
191
+ ``_unpack_event`` -- the (str, dict) shape stays supported via
192
+ ``WorkflowEvent.from_legacy`` upstream of the dispatcher.
193
+
194
+ Locks the contract so future refactors don't drop the envelope
195
+ path silently.
196
+ """
197
+
198
+ @pytest.fixture
199
+ def event_waiter_module(self):
200
+ from services import event_waiter
201
+
202
+ return event_waiter
203
+
204
+ def test_unpack_event_present(self, event_waiter_module):
205
+ assert hasattr(event_waiter_module, "_unpack_event"), (
206
+ "event_waiter must expose _unpack_event -- the normalisation "
207
+ "helper that lets dispatch() accept a WorkflowEvent directly."
208
+ )
209
+
210
+ def test_dispatch_calls_unpack_event(self, event_waiter_module):
211
+ src = inspect.getsource(event_waiter_module.dispatch)
212
+ assert "_unpack_event" in src, (
213
+ "dispatch() must route through _unpack_event() so a "
214
+ "WorkflowEvent argument is normalised before the dispatch loop."
215
+ )
216
+
217
+ def test_dispatch_async_calls_unpack_event(self, event_waiter_module):
218
+ src = inspect.getsource(event_waiter_module.dispatch_async)
219
+ assert "_unpack_event" in src, (
220
+ "dispatch_async() must route through _unpack_event() for the "
221
+ "same reason as dispatch()."
222
+ )
223
+
224
+ def test_unpack_event_handles_workflow_event(self, event_waiter_module):
225
+ """Functional check: a WorkflowEvent input round-trips to
226
+ ``(event_type, data)``. The legacy tuple-form path is exercised
227
+ by the rest of the test suite."""
228
+ from services.events.envelope import WorkflowEvent
229
+
230
+ event = WorkflowEvent(
231
+ source="machinaos://test",
232
+ type="test.something.happened",
233
+ subject="test-subject",
234
+ data={"k": "v"},
235
+ )
236
+ event_type, data = event_waiter_module._unpack_event(event)
237
+ assert event_type == "test.something.happened"
238
+ assert data == {"k": "v"}
239
+
240
+ def test_unpack_event_handles_legacy_tuple_form(self, event_waiter_module):
241
+ event_type, data = event_waiter_module._unpack_event(
242
+ "legacy.event.type", {"k": "v"},
243
+ )
244
+ assert event_type == "legacy.event.type"
245
+ assert data == {"k": "v"}
246
+
247
+ def test_unpack_event_rejects_unsupported_form(self, event_waiter_module):
248
+ with pytest.raises(TypeError):
249
+ event_waiter_module._unpack_event(42)
250
+
251
+
252
+ # ============================================================================
253
+ # 3) Plugin status broadcasts: typed builder enforced (with carve-outs)
254
+ # ============================================================================
255
+
256
+
257
+ class TestStatusBroadcastsUseTypedBuilder:
258
+ """Every ``_refresh_*`` / ``update_*_status`` method on
259
+ :class:`StatusBroadcaster` MUST broadcast via a ``WorkflowEvent``
260
+ OR be listed in :data:`_TELEMETRY_CARVE_OUT` (permanent exempt)
261
+ OR :data:`_LEGACY_RAW_DICT_BROADCASTS` (per-plugin migration TODO).
262
+
263
+ The carve-outs exist so the test stays green during incremental
264
+ migration; new methods that don't fit either carve-out fail CI.
265
+ """
266
+
267
+ @pytest.fixture
268
+ def broadcaster_methods(self) -> dict[str, str]:
269
+ from services import status_broadcaster as sb
270
+
271
+ cls_src = inspect.getsource(sb.StatusBroadcaster)
272
+ return _split_methods(cls_src)
273
+
274
+ def test_no_overlap_between_carve_outs(self):
275
+ """A method can't be both telemetry-permanent-exempt and
276
+ legacy-grandfathered. If it appears in both, one of the two
277
+ lists is wrong."""
278
+ overlap = _TELEMETRY_CARVE_OUT & _LEGACY_RAW_DICT_BROADCASTS
279
+ assert not overlap, (
280
+ f"Methods in both _TELEMETRY_CARVE_OUT and "
281
+ f"_LEGACY_RAW_DICT_BROADCASTS: {sorted(overlap)}. Pick one."
282
+ )
283
+
284
+ def test_status_methods_use_typed_builder_or_listed(
285
+ self, broadcaster_methods,
286
+ ):
287
+ offenders = []
288
+ for name, body in broadcaster_methods.items():
289
+ if not _REFRESH_OR_STATUS_NAME.match(name):
290
+ continue
291
+ if name in _TELEMETRY_CARVE_OUT:
292
+ continue
293
+ if name in _LEGACY_RAW_DICT_BROADCASTS:
294
+ continue
295
+ if not _is_typed_broadcast(body):
296
+ offenders.append(name)
297
+ assert not offenders, (
298
+ f"StatusBroadcaster methods {offenders} broadcast via raw "
299
+ f"{{type: ..., data: ...}} dicts. Either use WorkflowEvent / "
300
+ f"broadcast_credential_event, or document the exception in "
301
+ f"_TELEMETRY_CARVE_OUT (permanent exempt) or "
302
+ f"_LEGACY_RAW_DICT_BROADCASTS (per-plugin migration TODO) "
303
+ f"in tests/test_status_broadcasts.py."
304
+ )
305
+
306
+ def test_legacy_entries_actually_exist_on_class(self, broadcaster_methods):
307
+ """If we drop a legacy method during migration, remove its
308
+ allowlist entry too. Catches stale carve-outs."""
309
+ missing = _LEGACY_RAW_DICT_BROADCASTS - set(broadcaster_methods.keys())
310
+ assert not missing, (
311
+ f"_LEGACY_RAW_DICT_BROADCASTS lists methods that don't exist "
312
+ f"on StatusBroadcaster: {sorted(missing)}. Remove the stale "
313
+ f"entries from tests/test_status_broadcasts.py."
314
+ )
315
+
316
+ def test_telemetry_entries_actually_exist_on_class(self, broadcaster_methods):
317
+ """Same staleness check for the telemetry carve-out."""
318
+ missing = _TELEMETRY_CARVE_OUT - set(broadcaster_methods.keys())
319
+ assert not missing, (
320
+ f"_TELEMETRY_CARVE_OUT lists methods that don't exist "
321
+ f"on StatusBroadcaster: {sorted(missing)}. Remove the stale "
322
+ f"entries from tests/test_status_broadcasts.py."
323
+ )
324
+
325
+
326
+ # ============================================================================
327
+ # 4) send_custom_event callsite ratchet
328
+ # ============================================================================
329
+
330
+
331
+ class TestSendCustomEventPayload:
332
+ """``broadcaster.send_custom_event(event_type, data)`` accepts
333
+ either a raw dict or a ``WorkflowEvent`` today. Wave 11.I,
334
+ milestone U adds this ratchet so NEW callsites must pass a
335
+ WorkflowEvent unless explicitly listed in
336
+ :data:`_LEGACY_RAW_DICT_CALLSITES` with a documented WHY.
337
+ """
338
+
339
+ @pytest.fixture
340
+ def callsite_files(self) -> list[str]:
341
+ """All files under ``server/`` containing ``send_custom_event``."""
342
+ from pathlib import Path
343
+
344
+ root = Path(__file__).resolve().parent.parent # server/
345
+ return [
346
+ str(p.relative_to(root)).replace("\\", "/")
347
+ for p in root.rglob("*.py")
348
+ if "send_custom_event" in p.read_text(encoding="utf-8")
349
+ and "tests/" not in str(p).replace("\\", "/")
350
+ and p.name != "test_status_broadcasts.py"
351
+ ]
352
+
353
+ def test_definition_lives_on_status_broadcaster(self):
354
+ """``send_custom_event`` is defined on the broadcaster --
355
+ anchor the symbol so allowlist entries can point to caller
356
+ files without colliding with the definition."""
357
+ from services.status_broadcaster import StatusBroadcaster
358
+
359
+ assert hasattr(StatusBroadcaster, "send_custom_event")
360
+
361
+ def test_callsites_match_allowlist(self, callsite_files):
362
+ """Every file calling ``send_custom_event`` MUST be in the
363
+ legacy allowlist (with documented WHY) OR call it through a
364
+ WorkflowEvent envelope (caught by the next test).
365
+
366
+ New plugins that need to broadcast custom events should
367
+ either use :class:`WorkflowEvent` directly (preferred) or add
368
+ themselves to :data:`_LEGACY_RAW_DICT_CALLSITES` with a
369
+ comment explaining why typed-envelope migration is deferred.
370
+ """
371
+ # The status broadcaster file owns the definition; not a "caller".
372
+ callers = {
373
+ f for f in callsite_files
374
+ if not f.endswith("status_broadcaster.py")
375
+ }
376
+ unknown = callers - _LEGACY_RAW_DICT_CALLSITES
377
+ # New callers must EITHER appear in the allowlist OR pass a
378
+ # WorkflowEvent -- the next test verifies the envelope
379
+ # path; here we only flag truly new files.
380
+ offenders = []
381
+ for path in unknown:
382
+ full = Path_resolve(path)
383
+ src = full.read_text(encoding="utf-8")
384
+ if not _passes_workflow_event(src):
385
+ offenders.append(path)
386
+ assert not offenders, (
387
+ f"send_custom_event callsites missing from typed-envelope "
388
+ f"path AND not listed in _LEGACY_RAW_DICT_CALLSITES: "
389
+ f"{sorted(offenders)}. Either pass a WorkflowEvent or "
390
+ f"add the file path to the allowlist with documented WHY "
391
+ f"in tests/test_status_broadcasts.py."
392
+ )
393
+
394
+ def test_legacy_callsites_actually_call_send_custom_event(self, callsite_files):
395
+ """Stale-entry check: every path in
396
+ ``_LEGACY_RAW_DICT_CALLSITES`` must still contain a
397
+ ``send_custom_event`` call. Catches obsolete allowlist entries
398
+ that should have been removed during a migration."""
399
+ missing = _LEGACY_RAW_DICT_CALLSITES - set(callsite_files)
400
+ assert not missing, (
401
+ f"_LEGACY_RAW_DICT_CALLSITES lists files that no longer "
402
+ f"call send_custom_event: {sorted(missing)}. Remove the "
403
+ f"stale entries."
404
+ )
405
+
406
+
407
+ # ============================================================================
408
+ # Module-private helpers (used by the test bodies above)
409
+ # ============================================================================
410
+
411
+
412
+ def Path_resolve(rel: str):
413
+ """Resolve a server-relative path to an absolute :class:`pathlib.Path`."""
414
+ from pathlib import Path
415
+
416
+ return Path(__file__).resolve().parent.parent / rel
417
+
418
+
419
+ def _passes_workflow_event(src: str) -> bool:
420
+ """Heuristic: file imports WorkflowEvent AND every send_custom_event
421
+ call is made through a typed envelope. The simplest pattern is to
422
+ import WorkflowEvent and pass a constructed instance. We accept any
423
+ file that imports WorkflowEvent at all -- the test is a nudge, not
424
+ an exhaustive contract."""
425
+ return "WorkflowEvent" in src