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,393 @@
1
+ /**
2
+ * sound.ts — per-theme WebAudio sound packs.
3
+ *
4
+ * Synthesizes click / hover / type / success / error / run / save /
5
+ * modalOpen / modalClose events via a single OscillatorNode + GainNode
6
+ * per call. No external samples; no module-level audio context until
7
+ * the first play(). Off by default — toggle with `Sounds.setEnabled`.
8
+ *
9
+ * The active pack is selected by name (matches the per-theme
10
+ * `--sound-pack` CSS token). React glue lives in
11
+ * client/src/hooks/useSound.ts which:
12
+ * 1. Reads `--sound-pack` from `:root` whenever the active theme
13
+ * changes and calls `Sounds.setPack(...)`.
14
+ * 2. Wires `Sounds.setEnabled(...)` to the `soundEnabled` Zustand
15
+ * slice (persisted to localStorage as `machinaos-sound`).
16
+ *
17
+ * Ported from design_handoff_machinaos_themes/app/sound.js. The DOM
18
+ * autocapture (`document.addEventListener('click', ...)`) at the
19
+ * bottom of the upstream module is intentionally dropped — in React
20
+ * we fire `play()` from explicit handlers (ActionButton onClick,
21
+ * Modal open/close effect, etc.) so the side-effect surface is
22
+ * traceable.
23
+ */
24
+
25
+ export type SoundEvent =
26
+ | 'click'
27
+ | 'hover'
28
+ | 'type'
29
+ | 'success'
30
+ | 'error'
31
+ | 'run'
32
+ | 'save'
33
+ | 'modalOpen'
34
+ | 'modalClose';
35
+
36
+ export type SoundPackName =
37
+ | 'none'
38
+ | 'parchment'
39
+ | 'marble'
40
+ | 'ink'
41
+ | 'clockwork'
42
+ | 'vibraphone'
43
+ | 'terminal'
44
+ | 'scrap'
45
+ | 'crypt'
46
+ | 'bell'
47
+ | 'telex';
48
+
49
+ interface OscConfig {
50
+ type: OscillatorType;
51
+ freq: number;
52
+ dur: number;
53
+ vol: number;
54
+ attack: number;
55
+ decay: number;
56
+ /** Optional low-pass filter cutoff (Hz). */
57
+ lp?: number;
58
+ /** Optional frequency sweep multiplier; final = freq * sweep. */
59
+ sweep?: number;
60
+ }
61
+
62
+ type EventTable = Partial<Record<SoundEvent, OscConfig>>;
63
+
64
+ // Singleton AudioContext lazily constructed on first play.
65
+ let audioCtx: AudioContext | null = null;
66
+ function ensureCtx(): AudioContext | null {
67
+ if (audioCtx) return audioCtx;
68
+ try {
69
+ const Ctx = window.AudioContext || (window as any).webkitAudioContext;
70
+ if (!Ctx) return null;
71
+ audioCtx = new Ctx();
72
+ return audioCtx;
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+
78
+ let enabled = false;
79
+ let activePackName: SoundPackName = 'none';
80
+ let activePack: EventTable = {};
81
+
82
+ /**
83
+ * Per-event last-fire timestamps for throttling. Currently only `type`
84
+ * is throttled — rapid typing into a long input shouldn't queue dozens
85
+ * of OscillatorNodes per second. Other events (click, hover, success,
86
+ * etc.) are user-paced and don't need throttling.
87
+ *
88
+ * Uses `performance.now()` for monotonic timing. The 30 ms window
89
+ * matches the upstream `app/sound.js` reference engine's documented
90
+ * hover debounce; we apply the same to `type` since each oscillator
91
+ * costs ~25 ms decay on the parchment / marble packs.
92
+ */
93
+ const lastFireMs: Partial<Record<SoundEvent, number>> = {};
94
+ const THROTTLE_MS: Partial<Record<SoundEvent, number>> = {
95
+ type: 30,
96
+ hover: 30,
97
+ };
98
+
99
+ function play(cfg: OscConfig | undefined): void {
100
+ if (!cfg || !enabled) return;
101
+ const ac = ensureCtx();
102
+ if (!ac) return;
103
+ // If suspended, defer oscillator scheduling until resume() settles.
104
+ // Synchronously scheduled oscillators on a suspended context are
105
+ // silently dropped per the WebAudio autoplay policy
106
+ // (https://developer.chrome.com/blog/autoplay/#webaudio).
107
+ const schedule = () => {
108
+ try {
109
+ const osc = ac.createOscillator();
110
+ const gain = ac.createGain();
111
+ osc.type = cfg.type;
112
+ osc.frequency.value = cfg.freq;
113
+ let target: AudioNode = osc;
114
+ if (cfg.lp) {
115
+ const lp = ac.createBiquadFilter();
116
+ lp.type = 'lowpass';
117
+ lp.frequency.value = cfg.lp;
118
+ target.connect(lp);
119
+ target = lp;
120
+ }
121
+ target.connect(gain);
122
+ gain.connect(ac.destination);
123
+ const now = ac.currentTime;
124
+ gain.gain.setValueAtTime(0, now);
125
+ gain.gain.linearRampToValueAtTime(cfg.vol, now + cfg.attack);
126
+ gain.gain.exponentialRampToValueAtTime(0.0001, now + cfg.attack + cfg.decay);
127
+ if (cfg.sweep) {
128
+ osc.frequency.exponentialRampToValueAtTime(cfg.freq * cfg.sweep, now + cfg.dur);
129
+ }
130
+ osc.start(now);
131
+ osc.stop(now + cfg.dur + 0.02);
132
+ } catch {
133
+ // WebAudio errors are non-fatal — silently drop.
134
+ }
135
+ };
136
+ if (ac.state === 'running') {
137
+ schedule();
138
+ } else {
139
+ void Promise.resolve(ac.resume()).then(schedule).catch(() => {/* noop */});
140
+ }
141
+ }
142
+
143
+ // ── Sound packs ──────────────────────────────────────────────────────────
144
+ //
145
+ // Each pack maps each `SoundEvent` to an oscillator config. Volume is
146
+ // kept ≤ 0.10 across the board so no pack clips. Configs are ported
147
+ // verbatim from app/sound.js (handoff bundle).
148
+
149
+ const PARCHMENT: EventTable = {
150
+ click: { type: 'sine', freq: 220, dur: 0.04, vol: 0.06, attack: 0.005, decay: 0.04 },
151
+ hover: { type: 'triangle', freq: 380, dur: 0.02, vol: 0.025, attack: 0.002, decay: 0.02 },
152
+ type: { type: 'square', freq: 180, dur: 0.025, vol: 0.04, attack: 0.001, decay: 0.025, lp: 800 },
153
+ success: { type: 'sine', freq: 523, dur: 0.4, vol: 0.08, attack: 0.01, decay: 0.4, sweep: 1.5 },
154
+ error: { type: 'sawtooth', freq: 110, dur: 0.25, vol: 0.10, attack: 0.005, decay: 0.25, lp: 600 },
155
+ run: { type: 'sine', freq: 392, dur: 0.18, vol: 0.10, attack: 0.01, decay: 0.18, sweep: 1.3 },
156
+ save: { type: 'triangle', freq: 294, dur: 0.3, vol: 0.10, attack: 0.02, decay: 0.3 },
157
+ modalOpen: { type: 'sine', freq: 392, dur: 0.18, vol: 0.07, attack: 0.02, decay: 0.18, sweep: 1.2 },
158
+ modalClose: { type: 'sine', freq: 392, dur: 0.14, vol: 0.06, attack: 0.005, decay: 0.14, sweep: 0.7 },
159
+ };
160
+
161
+ const MARBLE: EventTable = {
162
+ click: { type: 'sine', freq: 330, dur: 0.05, vol: 0.07, attack: 0.001, decay: 0.05 },
163
+ hover: { type: 'sine', freq: 520, dur: 0.02, vol: 0.025, attack: 0.001, decay: 0.02 },
164
+ type: { type: 'triangle', freq: 260, dur: 0.03, vol: 0.04, attack: 0.001, decay: 0.03, lp: 1200 },
165
+ success: { type: 'sine', freq: 392, dur: 0.5, vol: 0.09, attack: 0.005, decay: 0.5, sweep: 1.6 },
166
+ error: { type: 'sawtooth', freq: 130, dur: 0.30, vol: 0.10, attack: 0.005, decay: 0.30, lp: 500 },
167
+ run: { type: 'sine', freq: 440, dur: 0.22, vol: 0.10, attack: 0.005, decay: 0.22, sweep: 1.4 },
168
+ save: { type: 'sine', freq: 330, dur: 0.35, vol: 0.10, attack: 0.01, decay: 0.35 },
169
+ modalOpen: { type: 'sine', freq: 392, dur: 0.20, vol: 0.07, attack: 0.02, decay: 0.20, sweep: 1.3 },
170
+ modalClose: { type: 'sine', freq: 392, dur: 0.16, vol: 0.06, attack: 0.005, decay: 0.16, sweep: 0.7 },
171
+ };
172
+
173
+ const INK: EventTable = {
174
+ click: { type: 'triangle', freq: 280, dur: 0.05, vol: 0.05, attack: 0.005, decay: 0.05, lp: 1500 },
175
+ hover: { type: 'sine', freq: 440, dur: 0.018, vol: 0.02, attack: 0.001, decay: 0.018 },
176
+ type: { type: 'sine', freq: 220, dur: 0.04, vol: 0.04, attack: 0.005, decay: 0.04, lp: 600 },
177
+ success: { type: 'sine', freq: 660, dur: 1.2, vol: 0.08, attack: 0.01, decay: 1.2 },
178
+ error: { type: 'triangle', freq: 174, dur: 0.4, vol: 0.08, attack: 0.005, decay: 0.4, lp: 800 },
179
+ run: { type: 'sine', freq: 523, dur: 0.6, vol: 0.08, attack: 0.02, decay: 0.6 },
180
+ save: { type: 'sine', freq: 440, dur: 0.8, vol: 0.10, attack: 0.01, decay: 0.8 },
181
+ modalOpen: { type: 'sine', freq: 523, dur: 0.4, vol: 0.06, attack: 0.04, decay: 0.4 },
182
+ modalClose: { type: 'sine', freq: 392, dur: 0.4, vol: 0.05, attack: 0.005, decay: 0.4 },
183
+ };
184
+
185
+ const CLOCKWORK: EventTable = {
186
+ click: { type: 'square', freq: 800, dur: 0.04, vol: 0.06, attack: 0.001, decay: 0.04, lp: 2200 },
187
+ hover: { type: 'square', freq: 1100, dur: 0.012, vol: 0.02, attack: 0.001, decay: 0.012 },
188
+ type: { type: 'square', freq: 600, dur: 0.025, vol: 0.05, attack: 0.001, decay: 0.025, lp: 1800 },
189
+ success: { type: 'sawtooth', freq: 523, dur: 0.5, vol: 0.08, attack: 0.005, decay: 0.5, sweep: 1.5, lp: 2500 },
190
+ error: { type: 'sawtooth', freq: 196, dur: 0.35, vol: 0.10, attack: 0.005, decay: 0.35, lp: 700 },
191
+ run: { type: 'square', freq: 440, dur: 0.30, vol: 0.10, attack: 0.005, decay: 0.30, sweep: 1.4, lp: 2000 },
192
+ save: { type: 'triangle', freq: 660, dur: 0.45, vol: 0.10, attack: 0.01, decay: 0.45, lp: 2400 },
193
+ modalOpen: { type: 'square', freq: 392, dur: 0.18, vol: 0.07, attack: 0.005, decay: 0.18, sweep: 1.3, lp: 1800 },
194
+ modalClose: { type: 'square', freq: 392, dur: 0.14, vol: 0.06, attack: 0.005, decay: 0.14, sweep: 0.65, lp: 1400 },
195
+ };
196
+
197
+ const VIBRAPHONE: EventTable = {
198
+ click: { type: 'triangle', freq: 880, dur: 0.10, vol: 0.06, attack: 0.001, decay: 0.10 },
199
+ hover: { type: 'triangle', freq: 1320, dur: 0.05, vol: 0.025, attack: 0.001, decay: 0.05 },
200
+ type: { type: 'sine', freq: 660, dur: 0.06, vol: 0.04, attack: 0.001, decay: 0.06 },
201
+ success: { type: 'triangle', freq: 988, dur: 0.5, vol: 0.09, attack: 0.001, decay: 0.5, sweep: 1.5 },
202
+ error: { type: 'sawtooth', freq: 233, dur: 0.25, vol: 0.10, attack: 0.005, decay: 0.25, sweep: 0.6 },
203
+ run: { type: 'triangle', freq: 1175, dur: 0.30, vol: 0.10, attack: 0.001, decay: 0.30, sweep: 1.4 },
204
+ save: { type: 'triangle', freq: 880, dur: 0.40, vol: 0.10, attack: 0.001, decay: 0.40 },
205
+ modalOpen: { type: 'triangle', freq: 988, dur: 0.18, vol: 0.06, attack: 0.001, decay: 0.18, sweep: 1.4 },
206
+ modalClose: { type: 'triangle', freq: 988, dur: 0.18, vol: 0.06, attack: 0.001, decay: 0.18, sweep: 0.65 },
207
+ };
208
+
209
+ const TERMINAL: EventTable = {
210
+ click: { type: 'square', freq: 1200, dur: 0.025, vol: 0.06, attack: 0.001, decay: 0.025 },
211
+ hover: { type: 'square', freq: 1800, dur: 0.012, vol: 0.025, attack: 0.001, decay: 0.012 },
212
+ type: { type: 'square', freq: 880, dur: 0.018, vol: 0.05, attack: 0.0005, decay: 0.018 },
213
+ success: { type: 'square', freq: 1318, dur: 0.18, vol: 0.08, attack: 0.001, decay: 0.18, sweep: 1.5 },
214
+ error: { type: 'sawtooth', freq: 220, dur: 0.18, vol: 0.10, attack: 0.001, decay: 0.18, sweep: 0.5 },
215
+ run: { type: 'square', freq: 1568, dur: 0.10, vol: 0.10, attack: 0.001, decay: 0.10 },
216
+ save: { type: 'square', freq: 988, dur: 0.12, vol: 0.08, attack: 0.001, decay: 0.12, sweep: 1.2 },
217
+ modalOpen: { type: 'square', freq: 1175, dur: 0.06, vol: 0.06, attack: 0.001, decay: 0.06, sweep: 1.4 },
218
+ modalClose: { type: 'square', freq: 1175, dur: 0.06, vol: 0.06, attack: 0.001, decay: 0.06, sweep: 0.6 },
219
+ };
220
+
221
+ const SCRAP: EventTable = {
222
+ click: { type: 'sawtooth', freq: 180, dur: 0.05, vol: 0.07, attack: 0.0005, decay: 0.05, lp: 1400 },
223
+ hover: { type: 'sawtooth', freq: 320, dur: 0.012, vol: 0.025, attack: 0.0005, decay: 0.012, lp: 2000 },
224
+ type: { type: 'square', freq: 140, dur: 0.03, vol: 0.05, attack: 0.0005, decay: 0.03, lp: 800 },
225
+ success: { type: 'sawtooth', freq: 440, dur: 0.18, vol: 0.10, attack: 0.001, decay: 0.18, sweep: 1.3, lp: 1600 },
226
+ error: { type: 'sawtooth', freq: 90, dur: 0.5, vol: 0.12, attack: 0.001, decay: 0.5, lp: 400 },
227
+ run: { type: 'square', freq: 220, dur: 0.20, vol: 0.10, attack: 0.001, decay: 0.20, sweep: 1.2, lp: 1200 },
228
+ save: { type: 'sawtooth', freq: 330, dur: 0.18, vol: 0.10, attack: 0.001, decay: 0.18, lp: 1400 },
229
+ modalOpen: { type: 'sawtooth', freq: 196, dur: 0.10, vol: 0.07, attack: 0.001, decay: 0.10, lp: 1000 },
230
+ modalClose: { type: 'sawtooth', freq: 196, dur: 0.08, vol: 0.06, attack: 0.001, decay: 0.08, sweep: 0.5, lp: 700 },
231
+ };
232
+
233
+ const CRYPT: EventTable = {
234
+ click: { type: 'sine', freq: 220, dur: 0.08, vol: 0.06, attack: 0.005, decay: 0.08, lp: 600 },
235
+ hover: { type: 'sine', freq: 330, dur: 0.025, vol: 0.02, attack: 0.005, decay: 0.025, lp: 800 },
236
+ type: { type: 'sine', freq: 165, dur: 0.04, vol: 0.04, attack: 0.005, decay: 0.04, lp: 400 },
237
+ success: { type: 'triangle', freq: 261, dur: 0.8, vol: 0.08, attack: 0.02, decay: 0.8, sweep: 1.5, lp: 1200 },
238
+ error: { type: 'sawtooth', freq: 73, dur: 0.6, vol: 0.10, attack: 0.01, decay: 0.6, lp: 300 },
239
+ run: { type: 'sine', freq: 196, dur: 0.45, vol: 0.10, attack: 0.02, decay: 0.45, sweep: 1.3, lp: 800 },
240
+ save: { type: 'sine', freq: 174, dur: 0.7, vol: 0.10, attack: 0.05, decay: 0.7, lp: 700 },
241
+ modalOpen: { type: 'sine', freq: 220, dur: 0.5, vol: 0.07, attack: 0.05, decay: 0.5, lp: 600 },
242
+ modalClose: { type: 'sine', freq: 220, dur: 0.4, vol: 0.06, attack: 0.005, decay: 0.4, sweep: 0.6, lp: 500 },
243
+ };
244
+
245
+ const BELL: EventTable = {
246
+ click: { type: 'triangle', freq: 392, dur: 0.10, vol: 0.06, attack: 0.001, decay: 0.10, lp: 1500 },
247
+ hover: { type: 'sine', freq: 660, dur: 0.025, vol: 0.025, attack: 0.001, decay: 0.025 },
248
+ type: { type: 'square', freq: 220, dur: 0.025, vol: 0.04, attack: 0.001, decay: 0.025, lp: 800 },
249
+ success: { type: 'triangle', freq: 523, dur: 1.0, vol: 0.09, attack: 0.001, decay: 1.0 },
250
+ error: { type: 'sawtooth', freq: 110, dur: 0.6, vol: 0.10, attack: 0.005, decay: 0.6, lp: 500 },
251
+ run: { type: 'triangle', freq: 440, dur: 0.6, vol: 0.10, attack: 0.001, decay: 0.6 },
252
+ save: { type: 'triangle', freq: 392, dur: 0.9, vol: 0.10, attack: 0.001, decay: 0.9 },
253
+ modalOpen: { type: 'triangle', freq: 523, dur: 0.4, vol: 0.07, attack: 0.001, decay: 0.4 },
254
+ modalClose: { type: 'triangle', freq: 392, dur: 0.4, vol: 0.06, attack: 0.001, decay: 0.4 },
255
+ };
256
+
257
+ const TELEX: EventTable = {
258
+ click: { type: 'square', freq: 1500, dur: 0.012, vol: 0.07, attack: 0.0005, decay: 0.012 },
259
+ hover: { type: 'square', freq: 2200, dur: 0.006, vol: 0.02, attack: 0.0005, decay: 0.006 },
260
+ type: { type: 'square', freq: 1100, dur: 0.010, vol: 0.05, attack: 0.0005, decay: 0.010 },
261
+ success: { type: 'square', freq: 1318, dur: 0.10, vol: 0.08, attack: 0.0005, decay: 0.10 },
262
+ error: { type: 'sawtooth', freq: 220, dur: 0.4, vol: 0.12, attack: 0.0005, decay: 0.4 },
263
+ run: { type: 'square', freq: 1760, dur: 0.06, vol: 0.10, attack: 0.0005, decay: 0.06 },
264
+ save: { type: 'square', freq: 1175, dur: 0.08, vol: 0.08, attack: 0.0005, decay: 0.08 },
265
+ modalOpen: { type: 'square', freq: 988, dur: 0.05, vol: 0.07, attack: 0.0005, decay: 0.05 },
266
+ modalClose: { type: 'square', freq: 988, dur: 0.04, vol: 0.06, attack: 0.0005, decay: 0.04, sweep: 0.6 },
267
+ };
268
+
269
+ const PACKS: Record<SoundPackName, EventTable> = {
270
+ none: {},
271
+ parchment: PARCHMENT,
272
+ marble: MARBLE,
273
+ ink: INK,
274
+ clockwork: CLOCKWORK,
275
+ vibraphone: VIBRAPHONE,
276
+ terminal: TERMINAL,
277
+ scrap: SCRAP,
278
+ crypt: CRYPT,
279
+ bell: BELL,
280
+ telex: TELEX,
281
+ };
282
+
283
+ // ── Public API ───────────────────────────────────────────────────────────
284
+
285
+ export const Sounds = {
286
+ setEnabled(value: boolean): void {
287
+ enabled = value;
288
+ },
289
+ isEnabled(): boolean {
290
+ return enabled;
291
+ },
292
+ setPack(name: SoundPackName): void {
293
+ activePackName = name;
294
+ activePack = PACKS[name] ?? PACKS.none;
295
+ },
296
+ pack(): SoundPackName {
297
+ return activePackName;
298
+ },
299
+ /**
300
+ * Idempotent gesture-time unlock. Constructs the AudioContext (if not
301
+ * already constructed) and resumes it if the browser autoplay policy
302
+ * left it `suspended`. Safe to call repeatedly — `resume()` on a
303
+ * running context is a no-op.
304
+ *
305
+ * Modern Chrome / Safari require the resume() call to occur inside
306
+ * the same task as a user gesture (pointerdown / keydown / touchstart).
307
+ * `Sounds.play()` defensively calls resume() too, but state-change-
308
+ * driven plays (e.g. modal open from an effect) can land microseconds
309
+ * after the gesture frame and lose the very first sound. Calling
310
+ * `unlock()` from a once-listener on the gesture path closes that gap.
311
+ */
312
+ unlock(): void {
313
+ const ac = ensureCtx();
314
+ if (!ac) return;
315
+ if (ac.state === 'suspended') void ac.resume();
316
+ },
317
+ play(event: SoundEvent): void {
318
+ const throttle = THROTTLE_MS[event];
319
+ if (throttle !== undefined) {
320
+ const now = typeof performance !== 'undefined' ? performance.now() : Date.now();
321
+ const last = lastFireMs[event] ?? 0;
322
+ if (now - last < throttle) return;
323
+ lastFireMs[event] = now;
324
+ }
325
+ play(activePack[event]);
326
+ },
327
+ /**
328
+ * Snapshot the engine state for DevTools self-diagnosis. Returns an
329
+ * object the user can inspect from the browser console:
330
+ * `> Sounds.diagnose()` →
331
+ * { enabled, pack, eventsForPack, audioContextState, browserSupport }
332
+ * Common silent-audio modes:
333
+ * - `enabled: false` → user hasn't toggled Settings → Audio.
334
+ * - `pack: 'none'` → :root[data-theme="..."] declares chime/typo
335
+ * and the validator falls through to none.
336
+ * - `audioContextState: 'suspended'` after a click → a browser
337
+ * extension or autoplay-block policy is denying resume().
338
+ */
339
+ diagnose(): {
340
+ enabled: boolean;
341
+ pack: SoundPackName;
342
+ eventsForPack: string[];
343
+ cssSoundPack: string;
344
+ cssDataTheme: string | null;
345
+ audioContextState: string | 'no-ac' | 'no-audio-api';
346
+ browserSupport: boolean;
347
+ } {
348
+ const Ctx = typeof window !== 'undefined'
349
+ ? window.AudioContext || (window as any).webkitAudioContext
350
+ : undefined;
351
+ const cssSoundPack = typeof document !== 'undefined'
352
+ ? getComputedStyle(document.documentElement).getPropertyValue('--sound-pack').trim().replace(/['"]/g, '')
353
+ : '';
354
+ const cssDataTheme = typeof document !== 'undefined'
355
+ ? document.documentElement.getAttribute('data-theme')
356
+ : null;
357
+ return {
358
+ enabled,
359
+ pack: activePackName,
360
+ eventsForPack: Object.keys(activePack),
361
+ cssSoundPack,
362
+ cssDataTheme,
363
+ audioContextState: !Ctx ? 'no-audio-api' : audioCtx ? audioCtx.state : 'no-ac',
364
+ browserSupport: !!Ctx,
365
+ };
366
+ },
367
+ /**
368
+ * Force a sound to play for testing — bypasses the `enabled` gate.
369
+ * Call from DevTools: `Sounds.test('click')` after the AC has unlocked.
370
+ * Returns whether the play scheduled successfully.
371
+ */
372
+ test(event: SoundEvent = 'click'): boolean {
373
+ const cfg = activePack[event];
374
+ if (!cfg) return false;
375
+ const ac = ensureCtx();
376
+ if (!ac) return false;
377
+ const wasEnabled = enabled;
378
+ enabled = true;
379
+ try {
380
+ play(cfg);
381
+ return true;
382
+ } finally {
383
+ enabled = wasEnabled;
384
+ }
385
+ },
386
+ };
387
+
388
+ // Expose on `window` in dev mode so users can self-diagnose silent audio
389
+ // from DevTools (`> Sounds.diagnose()`, `> Sounds.test()`). No-op in
390
+ // production builds — Vite tree-shakes the `import.meta.env.DEV` branch.
391
+ if (typeof window !== 'undefined' && import.meta.env.DEV) {
392
+ (window as any).Sounds = Sounds;
393
+ }
@@ -2,6 +2,26 @@ import { StrictMode } from 'react'
2
2
  import { createRoot } from 'react-dom/client'
3
3
  import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
4
4
  import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
5
+ // Theme CSS — load BEFORE index.css so the per-theme [data-theme="..."]
6
+ // blocks register their tokens before Tailwind compiles `@theme inline`
7
+ // against the cascade. Order within the theme list matches specificity:
8
+ // base.css (neutral defaults) → light.css (default :root + [data-theme="light"])
9
+ // → dark.css (.dark + [data-theme="dark"]) → renaissance + cyber overrides.
10
+ import './themes/base.css'
11
+ import './themes/light.css'
12
+ import './themes/dark.css'
13
+ // Utopian set
14
+ import './themes/renaissance.css'
15
+ import './themes/greek.css'
16
+ import './themes/edo.css'
17
+ import './themes/steampunk.css'
18
+ import './themes/atomic.css'
19
+ // Dystopian set
20
+ import './themes/cyber.css'
21
+ import './themes/wasteland.css'
22
+ import './themes/rot.css'
23
+ import './themes/plague.css'
24
+ import './themes/surveillance.css'
5
25
  import './index.css'
6
26
  import App from './App'
7
27
  import { ThemeProvider } from './contexts/ThemeContext'
@@ -49,6 +49,13 @@ interface AppStore {
49
49
  componentPaletteVisible: boolean;
50
50
  consolePanelVisible: boolean;
51
51
  proMode: boolean; // false = noob mode (only AI categories), true = pro mode (all categories)
52
+ /** WebAudio sound effects toggle (per-theme pack picked from
53
+ * --sound-pack CSS token by `useSoundSync()`). Persisted to
54
+ * localStorage as `machinaos-sound`; default ON (user disables in
55
+ * Settings -> Audio). The AudioContext starts suspended per
56
+ * browser autoplay policy — `Sounds.unlock()` resumes it on the
57
+ * user's first interaction (no separate audio permission needed). */
58
+ soundEnabled: boolean;
52
59
  renamingNodeId: string | null;
53
60
 
54
61
  // Workflow actions
@@ -65,6 +72,8 @@ interface AppStore {
65
72
  toggleSidebar: () => void;
66
73
  toggleComponentPalette: () => void;
67
74
  toggleProMode: () => void;
75
+ setSoundEnabled: (enabled: boolean) => void;
76
+ toggleSoundEnabled: () => void;
68
77
  setRenamingNodeId: (nodeId: string | null) => void;
69
78
 
70
79
  // UI defaults from database
@@ -130,6 +139,10 @@ const STORAGE_KEYS = {
130
139
  componentPaletteVisible: 'ui_component_palette_visible',
131
140
  consolePanelVisible: 'ui_console_panel_visible',
132
141
  proMode: 'ui_pro_mode',
142
+ /** Sound enabled key — matches the design handoff's
143
+ * `localStorage['machinaos-sound']` convention so a returning user's
144
+ * prior choice rehydrates regardless of which session set it. */
145
+ soundEnabled: 'machinaos-sound',
133
146
  };
134
147
 
135
148
  // Helper to load boolean from localStorage
@@ -163,6 +176,7 @@ export const useAppStore = create<AppStore>((set, get) => ({
163
176
  componentPaletteVisible: loadBooleanFromStorage(STORAGE_KEYS.componentPaletteVisible, true),
164
177
  consolePanelVisible: loadBooleanFromStorage(STORAGE_KEYS.consolePanelVisible, false),
165
178
  proMode: loadBooleanFromStorage(STORAGE_KEYS.proMode, false), // Default to noob mode
179
+ soundEnabled: loadBooleanFromStorage(STORAGE_KEYS.soundEnabled, true), // On by default; user can disable in Settings -> Audio. Browsers gesture-gate WebAudio (no separate permission), so the AC unlocks on first interaction via Sounds.unlock().
166
180
  renamingNodeId: null,
167
181
 
168
182
  // Workflow management
@@ -331,6 +345,18 @@ export const useAppStore = create<AppStore>((set, get) => ({
331
345
  });
332
346
  },
333
347
 
348
+ setSoundEnabled: (enabled) => {
349
+ saveBooleanToStorage(STORAGE_KEYS.soundEnabled, enabled);
350
+ set({ soundEnabled: enabled });
351
+ },
352
+ toggleSoundEnabled: () => {
353
+ set((state) => {
354
+ const newValue = !state.soundEnabled;
355
+ saveBooleanToStorage(STORAGE_KEYS.soundEnabled, newValue);
356
+ return { soundEnabled: newValue };
357
+ });
358
+ },
359
+
334
360
  setRenamingNodeId: (nodeId) => {
335
361
  set({ renamingNodeId: nodeId });
336
362
  },
@@ -3,14 +3,26 @@
3
3
  * Dashboard. Split into named groups so a new status visual or keyframe
4
4
  * can be added without touching Dashboard.tsx.
5
5
  *
6
- * KEYFRAMES -- @keyframes definitions
6
+ * KEYFRAMES -- @keyframes definitions for edges
7
7
  * edgeStatusStyles(...) -- .react-flow__edge.{selected,executing,...}
8
- * nodeStatusStyles(...) -- .react-flow__node.{executing,...}
8
+ * nodeStatusStyles(...) -- .react-flow__node.{...} (status-class colors only)
9
9
  * buildCanvasStyles(...) -- composes the three for Dashboard
10
10
  *
11
11
  * Per-node inline animations (border pulse, etc.) live in their own
12
12
  * components and read theme tokens directly; this module is for
13
13
  * canvas-wide rules that need to match React Flow's wrapper classes.
14
+ *
15
+ * Node execution glow is owned by `client/src/themes/base.css` — see
16
+ * the `node-pulse` keyframe + `.react-flow__node.executing .node` /
17
+ * `.sq-node[data-executing] .sq-node-box` rules there. This file used
18
+ * to inject a competing `nodeGlow` keyframe targeting the React Flow
19
+ * wrapper; that was dead code (only the inner `.node` child animated)
20
+ * and has been removed in favour of base.css as the single source of
21
+ * truth.
22
+ *
23
+ * The light vs dark distinction is encoded entirely in `colors` (the
24
+ * theme object provides different values per mode), so this file knows
25
+ * nothing about which theme is active.
14
26
  */
15
27
 
16
28
  export interface CanvasStatusColors {
@@ -19,6 +31,9 @@ export interface CanvasStatusColors {
19
31
  edgeExecuting: string;
20
32
  edgeCompleted: string;
21
33
  edgeError: string;
34
+ edgePending: string;
35
+ edgeMemoryActive: string;
36
+ edgeToolActive: string;
22
37
  }
23
38
 
24
39
  const KEYFRAMES = `
@@ -26,19 +41,9 @@ const KEYFRAMES = `
26
41
  0% { stroke-dashoffset: 24; }
27
42
  100% { stroke-dashoffset: 0; }
28
43
  }
29
-
30
- @keyframes nodeGlowDark {
31
- 0%, 100% { filter: drop-shadow(0 0 8px var(--node-glow)) drop-shadow(0 0 16px var(--node-glow-soft)); }
32
- 50% { filter: drop-shadow(0 0 14px var(--node-glow)) drop-shadow(0 0 24px var(--node-glow)); }
33
- }
34
-
35
- @keyframes nodeGlowLight {
36
- 0%, 100% { filter: drop-shadow(0 0 10px rgba(37, 99, 235, 0.8)) drop-shadow(0 0 20px rgba(37, 99, 235, 0.6)); }
37
- 50% { filter: drop-shadow(0 0 16px rgba(37, 99, 235, 1)) drop-shadow(0 0 30px rgba(37, 99, 235, 0.8)); }
38
- }
39
44
  `;
40
45
 
41
- function edgeStatusStyles(colors: CanvasStatusColors, isDark: boolean): string {
46
+ function edgeStatusStyles(colors: CanvasStatusColors): string {
42
47
  return `
43
48
  .react-flow__edge path {
44
49
  stroke: ${colors.edgeDefault} !important;
@@ -51,14 +56,14 @@ function edgeStatusStyles(colors: CanvasStatusColors, isDark: boolean): string {
51
56
  }
52
57
 
53
58
  .react-flow__edge.executing path {
54
- stroke: ${isDark ? colors.edgeExecuting : '#2563eb'} !important;
59
+ stroke: ${colors.edgeExecuting} !important;
55
60
  stroke-width: 3px !important;
56
61
  stroke-dasharray: 8 4;
57
62
  animation: dashFlow 0.5s linear infinite;
58
63
  }
59
64
 
60
65
  .react-flow__edge.completed path {
61
- stroke: ${isDark ? colors.edgeCompleted : '#16a34a'} !important;
66
+ stroke: ${colors.edgeCompleted} !important;
62
67
  stroke-width: 2px !important;
63
68
  }
64
69
 
@@ -68,45 +73,41 @@ function edgeStatusStyles(colors: CanvasStatusColors, isDark: boolean): string {
68
73
  }
69
74
 
70
75
  .react-flow__edge.pending path {
71
- stroke: ${isDark ? colors.edgeDefault : '#6b7280'} !important;
76
+ stroke: ${colors.edgePending} !important;
72
77
  stroke-width: 2px !important;
73
78
  stroke-dasharray: 8 4;
74
79
  animation: dashFlow 0.5s linear infinite;
75
80
  }
76
81
 
77
82
  .react-flow__edge.memory-active path {
78
- stroke: ${isDark ? '#ff79c6' : '#db2777'} !important;
83
+ stroke: ${colors.edgeMemoryActive} !important;
79
84
  stroke-width: 3px !important;
80
85
  }
81
86
 
82
87
  .react-flow__edge.tool-active path {
83
- stroke: ${isDark ? '#ffb86c' : '#ea580c'} !important;
88
+ stroke: ${colors.edgeToolActive} !important;
84
89
  stroke-width: 3px !important;
85
90
  }
86
91
  `;
87
92
  }
88
93
 
89
- function nodeStatusStyles(colors: CanvasStatusColors, isDark: boolean): string {
90
- // --node-glow / --node-glow-soft are scoped vars consumed by the
91
- // nodeGlowDark keyframe, so the keyframe stays color-agnostic and
92
- // theme swaps don't require regenerating the keyframe text.
93
- const glow = colors.edgeExecuting;
94
- return `
95
- .react-flow__node.executing {
96
- --node-glow: ${glow};
97
- --node-glow-soft: ${glow}80;
98
- animation: ${isDark ? 'nodeGlowDark' : 'nodeGlowLight'} 1.2s ease-in-out infinite;
99
- }
100
- `;
94
+ function nodeStatusStyles(_colors: CanvasStatusColors): string {
95
+ // Node execution animation is owned by base.css (`node-pulse`
96
+ // keyframe + `.react-flow__node.executing .node` /
97
+ // `.sq-node[data-executing] .sq-node-box` rules). This function is
98
+ // retained as a hook for future canvas-wide status-class rules on
99
+ // the React Flow wrapper that don't fit per-component CSS.
100
+ //
101
+ // `_colors` is intentionally unused at the moment; the parameter is
102
+ // kept on the signature so callers (buildCanvasStyles) and the
103
+ // CanvasStatusColors contract stay stable for downstream consumers.
104
+ return '';
101
105
  }
102
106
 
103
- export function buildCanvasStyles(
104
- colors: CanvasStatusColors,
105
- isDark: boolean,
106
- ): string {
107
+ export function buildCanvasStyles(colors: CanvasStatusColors): string {
107
108
  return [
108
- edgeStatusStyles(colors, isDark),
109
- nodeStatusStyles(colors, isDark),
109
+ edgeStatusStyles(colors),
110
+ nodeStatusStyles(colors),
110
111
  KEYFRAMES,
111
112
  ].join('\n');
112
113
  }