machinaos 0.0.78 → 0.0.80

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 (743) hide show
  1. package/.env.template +74 -5
  2. package/{workflows/AI Assistant_workflow-1778504793388-ou1m1tz2x.json → .machina/workflows/AI Assistant_example_workflow-1779017037684-e2e5da7a.json } +164 -105
  3. package/{workflows/AI Employee_example_workflow-1777720598005-u4cm858dv.json → .machina/workflows/AI Employee_example_workflow-1779102911870-cbc76c82.json } +582 -328
  4. package/{workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json → .machina/workflows/Claude Assistant_example_workflow-1779095939967-2369cff4.json } +152 -83
  5. package/README.md +5 -2
  6. package/bin/cli.js +2 -2
  7. package/{machina → cli}/__main__.py +11 -7
  8. package/cli/_common.py +122 -0
  9. package/cli/buildenv.py +40 -0
  10. package/cli/cli.py +204 -0
  11. package/{machina → cli}/colors.py +10 -2
  12. package/cli/commands/__init__.py +1 -0
  13. package/cli/commands/_temporal_specs.py +59 -0
  14. package/{machina → cli}/commands/build.py +35 -45
  15. package/cli/commands/clean.py +141 -0
  16. package/cli/commands/daemon/__init__.py +47 -0
  17. package/cli/commands/daemon/_state.py +97 -0
  18. package/cli/commands/daemon/restart.py +14 -0
  19. package/cli/commands/daemon/start.py +49 -0
  20. package/cli/commands/daemon/status.py +20 -0
  21. package/cli/commands/daemon/stop.py +22 -0
  22. package/{machina → cli}/commands/dev.py +32 -42
  23. package/{machina → cli}/commands/docs.py +13 -11
  24. package/{machina → cli}/commands/start.py +69 -62
  25. package/{machina → cli}/commands/stop.py +7 -10
  26. package/{machina → cli}/commands/version.py +12 -6
  27. package/cli/config.py +170 -0
  28. package/cli/platform_.py +169 -0
  29. package/{machina → cli}/ports.py +42 -3
  30. package/{machina → cli}/run.py +29 -2
  31. package/{machina → cli}/supervisor.py +29 -12
  32. package/{machina → cli}/tcp.py +6 -2
  33. package/{machina → cli}/tree.py +38 -11
  34. package/client/dist/assets/{ActionBar-Du2MSFSz.js → ActionBar-Cjr3TF7g.js} +1 -1
  35. package/client/dist/assets/{ApiKeyInput-k2LBmBjb.js → ApiKeyInput-DIJE2PVA.js} +1 -1
  36. package/client/dist/assets/{ApiKeyPanel-C_bV9U0X.js → ApiKeyPanel-CPmye7uh.js} +1 -1
  37. package/client/dist/assets/{ApiUsageSection-CmVfwZzL.js → ApiUsageSection-TF_7gH2D.js} +1 -1
  38. package/client/dist/assets/{EmailPanel-CeKIMGu-.js → EmailPanel-Bs-xvbKR.js} +1 -1
  39. package/client/dist/assets/{OAuthPanel-KA3t3Q2K.js → OAuthPanel-BDtVJhAV.js} +1 -1
  40. package/client/dist/assets/{QrPairingPanel-NgNpJNuk.js → QrPairingPanel-BwJehTuZ.js} +1 -1
  41. package/client/dist/assets/{RateLimitSection-Du5YNVIA.js → RateLimitSection-CfNOoPIS.js} +1 -1
  42. package/client/dist/assets/{StatusCard-DNLyayXc.js → StatusCard-DkwIrgdP.js} +1 -1
  43. package/client/dist/assets/index-P2FzntoL.js +165 -0
  44. package/client/dist/index.html +1 -1
  45. package/client/package.json +1 -1
  46. package/client/src/Dashboard.tsx +128 -76
  47. package/client/src/adapters/nodeSpecToDescription.ts +7 -0
  48. package/client/src/assets/icons/index.test.ts +10 -0
  49. package/client/src/assets/icons/index.ts +16 -3
  50. package/client/src/components/AIAgentNode.tsx +8 -8
  51. package/client/src/components/ParameterRenderer.tsx +6 -3
  52. package/client/src/components/SkillEditorModal.tsx +1 -0
  53. package/client/src/components/credentials/panels/EmailPanel.tsx +2 -0
  54. package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +2 -0
  55. package/client/src/components/credentials/sections/RateLimitSection.tsx +1 -0
  56. package/client/src/components/icons/AIProviderIcons.tsx +1 -0
  57. package/client/src/components/maps/GoogleMapsPicker.tsx +1 -0
  58. package/client/src/components/parameterPanel/InputSection.tsx +1 -0
  59. package/client/src/components/parameterPanel/MasterSkillEditor.tsx +1 -0
  60. package/client/src/components/parameterPanel/OutputSection.tsx +1 -0
  61. package/client/src/components/ui/ComponentPalette.tsx +1 -0
  62. package/client/src/components/ui/MapSelector.tsx +1 -0
  63. package/client/src/components/ui/NodeContextMenu.tsx +3 -3
  64. package/client/src/components/ui/SettingsPanel.tsx +1 -0
  65. package/client/src/components/ui/action-button.tsx +1 -0
  66. package/client/src/components/ui/badge.tsx +1 -0
  67. package/client/src/components/ui/button.tsx +1 -0
  68. package/client/src/components/ui/form.tsx +1 -0
  69. package/client/src/components/ui/tabs.tsx +1 -0
  70. package/client/src/contexts/AuthContext.tsx +1 -0
  71. package/client/src/contexts/ThemeContext.tsx +1 -0
  72. package/client/src/contexts/WebSocketContext.tsx +104 -34
  73. package/client/src/hooks/__tests__/useApiKeys.test.ts +2 -2
  74. package/client/src/hooks/useReactFlowNodes.ts +1 -0
  75. package/client/src/hooks/useWorkflowValidation.ts +142 -0
  76. package/client/src/lib/nodeSpec.ts +1 -0
  77. package/client/src/test/providers.tsx +1 -0
  78. package/client/src/types/__tests__/cloudEvents.test.ts +5 -2
  79. package/client/src/types/cloudEvents.ts +19 -7
  80. package/client/src/utils/nodeUtils.ts +1 -1
  81. package/client/src/utils/workflow.ts +8 -2
  82. package/client/src/utils/workflowExport.ts +60 -3
  83. package/package.json +24 -23
  84. package/scripts/install.js +16 -27
  85. package/scripts/migrate_icons.py +3 -1
  86. package/scripts/migrate_skill_icons.py +6 -7
  87. package/scripts/postinstall.js +11 -9
  88. package/server/config/ai_cli_providers.json +2 -3
  89. package/server/config/credential_providers.json +15 -15
  90. package/server/config/llm_defaults.json +1 -1
  91. package/server/config/model_registry.json +416 -611
  92. package/server/constants.py +285 -223
  93. package/server/core/__init__.py +1 -1
  94. package/server/core/cache.py +9 -29
  95. package/server/core/cleanup.py +12 -24
  96. package/server/core/config.py +148 -24
  97. package/server/core/container.py +68 -59
  98. package/server/core/credential_backends.py +5 -13
  99. package/server/core/credentials_database.py +13 -43
  100. package/server/core/database.py +292 -353
  101. package/server/core/health.py +4 -5
  102. package/server/core/logging.py +241 -87
  103. package/server/core/paths.py +285 -0
  104. package/server/core/tracing.py +2 -8
  105. package/server/gunicorn.conf.py +1 -0
  106. package/server/main.py +150 -74
  107. package/server/middleware/auth.py +18 -24
  108. package/server/models/__init__.py +1 -1
  109. package/server/models/auth.py +5 -12
  110. package/server/models/database.py +36 -68
  111. package/server/models/node_metadata.py +25 -18
  112. package/server/nodejs/dist/index.js +107 -0
  113. package/server/nodes/README.md +11 -5
  114. package/server/nodes/__init__.py +1 -1
  115. package/server/nodes/_visuals.py +146 -14
  116. package/server/nodes/agent/_events.py +124 -0
  117. package/server/nodes/agent/_handles.py +15 -29
  118. package/server/nodes/agent/_inline.py +28 -25
  119. package/server/nodes/agent/_specialized.py +30 -15
  120. package/server/nodes/agent/{ai_agent.py → ai_agent/__init__.py} +33 -17
  121. package/server/nodes/agent/ai_agent/meta.json +3 -0
  122. package/server/nodes/agent/{ai_employee.py → ai_employee/__init__.py} +5 -2
  123. package/server/nodes/agent/ai_employee/meta.json +3 -0
  124. package/server/nodes/agent/{android_agent.py → android_agent/__init__.py} +1 -1
  125. package/server/nodes/agent/android_agent/meta.json +3 -0
  126. package/server/nodes/agent/{autonomous_agent.py → autonomous_agent/__init__.py} +2 -1
  127. package/server/nodes/agent/autonomous_agent/meta.json +3 -0
  128. package/server/nodes/agent/{chat_agent.py → chat_agent/__init__.py} +29 -12
  129. package/server/nodes/agent/chat_agent/meta.json +3 -0
  130. package/server/nodes/agent/{claude_code_agent.py → claude_code_agent/__init__.py} +192 -95
  131. package/server/nodes/agent/claude_code_agent/_handlers.py +169 -0
  132. package/server/{services/claude_oauth.py → nodes/agent/claude_code_agent/_oauth.py} +26 -13
  133. package/server/nodes/agent/claude_code_agent/_pool.py +1020 -0
  134. package/server/nodes/agent/claude_code_agent/_provider.py +513 -0
  135. package/server/nodes/agent/claude_code_agent/_skills.py +245 -0
  136. package/server/nodes/agent/claude_code_agent/meta.json +3 -0
  137. package/server/nodes/agent/{codex_agent.py → codex_agent/__init__.py} +26 -35
  138. package/server/nodes/agent/codex_agent/meta.json +3 -0
  139. package/server/nodes/agent/{coding_agent.py → coding_agent/__init__.py} +1 -1
  140. package/server/nodes/agent/coding_agent/meta.json +3 -0
  141. package/server/nodes/agent/{consumer_agent.py → consumer_agent/__init__.py} +1 -1
  142. package/server/nodes/agent/consumer_agent/meta.json +3 -0
  143. package/server/nodes/agent/{orchestrator_agent.py → orchestrator_agent/__init__.py} +5 -2
  144. package/server/nodes/agent/orchestrator_agent/meta.json +3 -0
  145. package/server/nodes/agent/{payments_agent.py → payments_agent/__init__.py} +1 -1
  146. package/server/nodes/agent/payments_agent/meta.json +3 -0
  147. package/server/nodes/agent/{productivity_agent.py → productivity_agent/__init__.py} +1 -1
  148. package/server/nodes/agent/productivity_agent/meta.json +3 -0
  149. package/server/nodes/agent/{rlm_agent.py → rlm_agent/__init__.py} +18 -17
  150. package/server/nodes/agent/rlm_agent/meta.json +3 -0
  151. package/server/nodes/agent/{social_agent.py → social_agent/__init__.py} +1 -1
  152. package/server/nodes/agent/social_agent/meta.json +3 -0
  153. package/server/nodes/agent/{task_agent.py → task_agent/__init__.py} +1 -1
  154. package/server/nodes/agent/task_agent/meta.json +3 -0
  155. package/server/nodes/agent/{tool_agent.py → tool_agent/__init__.py} +1 -1
  156. package/server/nodes/agent/tool_agent/meta.json +3 -0
  157. package/server/nodes/agent/{travel_agent.py → travel_agent/__init__.py} +1 -1
  158. package/server/nodes/agent/travel_agent/meta.json +3 -0
  159. package/server/nodes/agent/{web_agent.py → web_agent/__init__.py} +1 -1
  160. package/server/nodes/agent/web_agent/meta.json +3 -0
  161. package/server/nodes/android/__init__.py +24 -0
  162. package/server/nodes/android/_base.py +93 -76
  163. package/server/nodes/android/_dispatcher.py +140 -223
  164. package/server/nodes/android/_events.py +154 -0
  165. package/server/nodes/android/_handlers.py +13 -7
  166. package/server/nodes/android/_option_loaders.py +1 -4
  167. package/server/nodes/android/_refresh.py +27 -37
  168. package/server/nodes/android/_relay/broadcaster.py +25 -41
  169. package/server/nodes/android/_relay/client.py +23 -42
  170. package/server/nodes/android/_relay/manager.py +1 -0
  171. package/server/nodes/android/_relay/protocol.py +6 -0
  172. package/server/nodes/android/_router.py +48 -133
  173. package/server/nodes/android/{airplane_mode_control.py → airplane_mode_control/__init__.py} +2 -1
  174. package/server/nodes/android/airplane_mode_control/meta.json +3 -0
  175. package/server/nodes/android/{app_launcher.py → app_launcher/__init__.py} +2 -1
  176. package/server/nodes/android/app_launcher/meta.json +3 -0
  177. package/server/nodes/android/{app_list.py → app_list/__init__.py} +2 -1
  178. package/server/nodes/android/app_list/meta.json +3 -0
  179. package/server/nodes/android/{audio_automation.py → audio_automation/__init__.py} +2 -1
  180. package/server/nodes/android/audio_automation/meta.json +3 -0
  181. package/server/nodes/android/{battery_monitor.py → battery_monitor/__init__.py} +2 -1
  182. package/server/nodes/android/battery_monitor/meta.json +3 -0
  183. package/server/nodes/android/{bluetooth_automation.py → bluetooth_automation/__init__.py} +2 -1
  184. package/server/nodes/android/bluetooth_automation/meta.json +3 -0
  185. package/server/nodes/android/{camera_control.py → camera_control/__init__.py} +2 -1
  186. package/server/nodes/android/camera_control/meta.json +3 -0
  187. package/server/nodes/android/{device_state_automation.py → device_state_automation/__init__.py} +2 -1
  188. package/server/nodes/android/device_state_automation/meta.json +3 -0
  189. package/server/nodes/android/{environmental_sensors.py → environmental_sensors/__init__.py} +2 -1
  190. package/server/nodes/android/environmental_sensors/meta.json +3 -0
  191. package/server/nodes/android/{location.py → location/__init__.py} +2 -1
  192. package/server/nodes/android/location/meta.json +3 -0
  193. package/server/nodes/android/{media_control.py → media_control/__init__.py} +2 -1
  194. package/server/nodes/android/media_control/meta.json +3 -0
  195. package/server/nodes/android/{motion_detection.py → motion_detection/__init__.py} +2 -1
  196. package/server/nodes/android/motion_detection/meta.json +3 -0
  197. package/server/nodes/android/{network_monitor.py → network_monitor/__init__.py} +2 -1
  198. package/server/nodes/android/network_monitor/meta.json +3 -0
  199. package/server/nodes/android/{screen_control_automation.py → screen_control_automation/__init__.py} +2 -1
  200. package/server/nodes/android/screen_control_automation/meta.json +3 -0
  201. package/server/nodes/android/{system_info.py → system_info/__init__.py} +2 -1
  202. package/server/nodes/android/system_info/meta.json +3 -0
  203. package/server/nodes/android/{wifi_automation.py → wifi_automation/__init__.py} +2 -1
  204. package/server/nodes/android/wifi_automation/meta.json +3 -0
  205. package/server/nodes/browser/__init__.py +22 -1
  206. package/server/nodes/browser/_install.py +63 -0
  207. package/server/nodes/browser/_service.py +21 -25
  208. package/server/nodes/browser/{browser.py → browser/__init__.py} +58 -25
  209. package/server/nodes/browser/browser/meta.json +3 -0
  210. package/server/nodes/chat/{chat_history.py → chat_history/__init__.py} +2 -4
  211. package/server/nodes/chat/chat_history/meta.json +3 -0
  212. package/server/nodes/chat/{chat_send.py → chat_send/__init__.py} +2 -4
  213. package/server/nodes/chat/chat_send/icon.svg +1 -0
  214. package/server/nodes/chat/chat_send/meta.json +3 -0
  215. package/server/nodes/code/_base.py +1 -1
  216. package/server/nodes/code/{javascript_executor.py → javascript_executor/__init__.py} +5 -5
  217. package/server/nodes/code/javascript_executor/meta.json +3 -0
  218. package/server/nodes/code/{python_executor.py → python_executor/__init__.py} +32 -14
  219. package/server/nodes/code/python_executor/meta.json +3 -0
  220. package/server/nodes/code/{typescript_executor.py → typescript_executor/__init__.py} +5 -5
  221. package/server/nodes/code/typescript_executor/meta.json +3 -0
  222. package/server/nodes/document/{document_parser.py → document_parser/__init__.py} +26 -15
  223. package/server/nodes/document/document_parser/meta.json +3 -0
  224. package/server/nodes/document/{embedding_generator.py → embedding_generator/__init__.py} +16 -9
  225. package/server/nodes/document/embedding_generator/meta.json +3 -0
  226. package/server/nodes/document/{file_downloader.py → file_downloader/__init__.py} +30 -20
  227. package/server/nodes/document/file_downloader/meta.json +3 -0
  228. package/server/nodes/document/{http_scraper.py → http_scraper/__init__.py} +31 -21
  229. package/server/nodes/document/http_scraper/meta.json +3 -0
  230. package/server/nodes/document/{text_chunker.py → text_chunker/__init__.py} +17 -12
  231. package/server/nodes/document/text_chunker/meta.json +3 -0
  232. package/server/nodes/document/{vector_store.py → vector_store/__init__.py} +88 -72
  233. package/server/nodes/document/vector_store/meta.json +3 -0
  234. package/server/nodes/email/__init__.py +9 -2
  235. package/server/nodes/email/_events.py +54 -0
  236. package/server/nodes/email/_filters.py +3 -3
  237. package/server/nodes/email/_himalaya.py +95 -50
  238. package/server/nodes/email/_service.py +23 -13
  239. package/server/nodes/email/{email_read.py → email_read/__init__.py} +23 -11
  240. package/server/nodes/email/email_read/icon.svg +6 -0
  241. package/server/nodes/email/email_read/meta.json +3 -0
  242. package/server/nodes/email/{email_receive.py → email_receive/__init__.py} +45 -23
  243. package/server/nodes/email/email_receive/meta.json +3 -0
  244. package/server/nodes/email/{email_send.py → email_send/__init__.py} +13 -7
  245. package/server/nodes/email/email_send/meta.json +3 -0
  246. package/server/nodes/filesystem/_backend.py +1 -5
  247. package/server/nodes/filesystem/{file_modify.py → file_modify/__init__.py} +10 -5
  248. package/server/nodes/filesystem/file_modify/meta.json +3 -0
  249. package/server/nodes/filesystem/{file_read.py → file_read/__init__.py} +7 -3
  250. package/server/nodes/filesystem/file_read/meta.json +3 -0
  251. package/server/nodes/filesystem/{fs_search.py → fs_search/__init__.py} +11 -3
  252. package/server/nodes/filesystem/fs_search/meta.json +3 -0
  253. package/server/nodes/filesystem/{shell.py → shell/__init__.py} +12 -5
  254. package/server/nodes/filesystem/shell/meta.json +3 -0
  255. package/server/nodes/google/__init__.py +12 -0
  256. package/server/nodes/google/_auth_helper.py +7 -13
  257. package/server/nodes/google/_base.py +14 -11
  258. package/server/nodes/google/_credentials.py +2 -1
  259. package/server/nodes/google/_events.py +47 -0
  260. package/server/nodes/google/_filters.py +3 -3
  261. package/server/nodes/google/_gmail.py +70 -47
  262. package/server/nodes/google/_handlers.py +3 -1
  263. package/server/nodes/google/_oauth.py +25 -11
  264. package/server/nodes/google/_option_loaders.py +9 -30
  265. package/server/nodes/google/_refresh.py +8 -12
  266. package/server/nodes/google/_router.py +4 -5
  267. package/server/nodes/google/{calendar.py → calendar/__init__.py} +87 -64
  268. package/server/nodes/google/calendar/meta.json +3 -0
  269. package/server/nodes/google/{contacts.py → contacts/__init__.py} +84 -72
  270. package/server/nodes/google/contacts/meta.json +3 -0
  271. package/server/nodes/google/{drive.py → drive/__init__.py} +87 -72
  272. package/server/nodes/google/drive/meta.json +3 -0
  273. package/server/nodes/google/{gmail.py → gmail/__init__.py} +73 -39
  274. package/server/nodes/google/gmail/meta.json +3 -0
  275. package/server/nodes/google/{gmail_receive.py → gmail_receive/__init__.py} +31 -24
  276. package/server/nodes/google/gmail_receive/icon.svg +7 -0
  277. package/server/nodes/google/gmail_receive/meta.json +3 -0
  278. package/server/nodes/google/google.svg +7 -0
  279. package/server/nodes/google/{sheets.py → sheets/__init__.py} +54 -42
  280. package/server/nodes/google/sheets/meta.json +3 -0
  281. package/server/nodes/google/{tasks.py → tasks/__init__.py} +56 -43
  282. package/server/nodes/google/tasks/meta.json +3 -0
  283. package/server/nodes/groups.py +28 -28
  284. package/server/nodes/location/__init__.py +31 -1
  285. package/server/nodes/location/_credentials.py +1 -6
  286. package/server/nodes/location/_service.py +88 -107
  287. package/server/nodes/location/{gmaps_create.py → gmaps_create/__init__.py} +6 -6
  288. package/server/nodes/location/gmaps_create/meta.json +3 -0
  289. package/server/nodes/location/{gmaps_locations.py → gmaps_locations/__init__.py} +8 -6
  290. package/server/nodes/location/gmaps_locations/meta.json +3 -0
  291. package/server/nodes/location/{gmaps_nearby_places.py → gmaps_nearby_places/__init__.py} +8 -6
  292. package/server/nodes/location/gmaps_nearby_places/meta.json +3 -0
  293. package/server/nodes/model/_base.py +10 -7
  294. package/server/nodes/model/_credentials.py +10 -10
  295. package/server/nodes/model/_local_validator.py +28 -24
  296. package/server/nodes/model/{anthropic_chat_model.py → anthropic_chat_model/__init__.py} +5 -3
  297. package/server/nodes/model/anthropic_chat_model/meta.json +3 -0
  298. package/server/nodes/model/{cerebras_chat_model.py → cerebras_chat_model/__init__.py} +5 -3
  299. package/server/nodes/model/cerebras_chat_model/meta.json +3 -0
  300. package/server/nodes/model/{deepseek_chat_model.py → deepseek_chat_model/__init__.py} +8 -4
  301. package/server/nodes/model/deepseek_chat_model/meta.json +3 -0
  302. package/server/nodes/model/{gemini_chat_model.py → gemini_chat_model/__init__.py} +5 -3
  303. package/server/nodes/model/gemini_chat_model/meta.json +3 -0
  304. package/server/nodes/model/{groq_chat_model.py → groq_chat_model/__init__.py} +2 -2
  305. package/server/nodes/model/groq_chat_model/meta.json +3 -0
  306. package/server/nodes/model/{kimi_chat_model.py → kimi_chat_model/__init__.py} +2 -2
  307. package/server/nodes/model/kimi_chat_model/meta.json +3 -0
  308. package/server/nodes/model/{lmstudio_chat_model.py → lmstudio_chat_model/__init__.py} +2 -2
  309. package/server/nodes/model/lmstudio_chat_model/meta.json +3 -0
  310. package/server/nodes/model/{mistral_chat_model.py → mistral_chat_model/__init__.py} +2 -2
  311. package/server/nodes/model/mistral_chat_model/meta.json +3 -0
  312. package/server/nodes/model/{ollama_chat_model.py → ollama_chat_model/__init__.py} +2 -2
  313. package/server/nodes/model/ollama_chat_model/meta.json +3 -0
  314. package/server/nodes/model/{openai_chat_model.py → openai_chat_model/__init__.py} +8 -4
  315. package/server/nodes/model/openai_chat_model/meta.json +3 -0
  316. package/server/nodes/model/{openrouter_chat_model.py → openrouter_chat_model/__init__.py} +8 -4
  317. package/server/nodes/model/openrouter_chat_model/meta.json +3 -0
  318. package/server/nodes/proxy/_usage.py +14 -15
  319. package/server/nodes/proxy/{proxy_config.py → proxy_config/__init__.py} +39 -30
  320. package/server/nodes/proxy/proxy_config/meta.json +3 -0
  321. package/server/nodes/proxy/{proxy_request.py → proxy_request/__init__.py} +30 -16
  322. package/server/nodes/proxy/proxy_request/meta.json +3 -0
  323. package/server/nodes/proxy/{proxy_status.py → proxy_status/__init__.py} +2 -0
  324. package/server/nodes/proxy/proxy_status/meta.json +3 -0
  325. package/server/nodes/scheduler/{cron_scheduler.py → cron_scheduler/__init__.py} +96 -23
  326. package/server/nodes/scheduler/cron_scheduler/_workflow.py +155 -0
  327. package/server/nodes/scheduler/cron_scheduler/meta.json +3 -0
  328. package/server/nodes/scheduler/{timer.py → timer/__init__.py} +6 -5
  329. package/server/nodes/scheduler/timer/meta.json +3 -0
  330. package/server/nodes/scraper/_credentials.py +0 -1
  331. package/server/nodes/scraper/{apify_actor.py → apify_actor/__init__.py} +44 -35
  332. package/server/nodes/scraper/apify_actor/icon.svg +5 -0
  333. package/server/nodes/scraper/apify_actor/meta.json +3 -0
  334. package/server/nodes/scraper/{crawlee_scraper.py → crawlee_scraper/__init__.py} +96 -57
  335. package/server/nodes/scraper/crawlee_scraper/meta.json +3 -0
  336. package/server/nodes/search/{brave_search.py → brave_search/__init__.py} +6 -5
  337. package/server/nodes/search/brave_search/icon.svg +3 -0
  338. package/server/nodes/search/brave_search/meta.json +3 -0
  339. package/server/nodes/search/{duckduckgo_search.py → duckduckgo_search/__init__.py} +17 -6
  340. package/server/nodes/search/duckduckgo_search/meta.json +3 -0
  341. package/server/nodes/search/{perplexity_search.py → perplexity_search/__init__.py} +4 -5
  342. package/server/nodes/search/perplexity_search/icon.svg +3 -0
  343. package/server/nodes/search/perplexity_search/meta.json +3 -0
  344. package/server/nodes/search/{serper_search.py → serper_search/__init__.py} +32 -25
  345. package/server/nodes/search/serper_search/icon.svg +3 -0
  346. package/server/nodes/search/serper_search/meta.json +3 -0
  347. package/server/nodes/skill/__init__.py +21 -1
  348. package/server/nodes/skill/_expander.py +75 -0
  349. package/server/nodes/skill/{master_skill.py → master_skill/__init__.py} +2 -8
  350. package/server/nodes/skill/master_skill/_events.py +84 -0
  351. package/server/nodes/skill/master_skill/meta.json +3 -0
  352. package/server/nodes/skill/{simple_memory.py → simple_memory/__init__.py} +8 -16
  353. package/server/nodes/skill/simple_memory/meta.json +3 -0
  354. package/server/nodes/social/_base.py +223 -231
  355. package/server/nodes/social/{social_receive.py → social_receive/__init__.py} +38 -13
  356. package/server/nodes/social/social_receive/meta.json +3 -0
  357. package/server/nodes/social/{social_send.py → social_send/__init__.py} +71 -29
  358. package/server/nodes/social/social_send/icon.svg +1 -0
  359. package/server/nodes/social/social_send/meta.json +3 -0
  360. package/server/nodes/stripe/__init__.py +7 -3
  361. package/server/nodes/stripe/_credentials.py +0 -1
  362. package/server/nodes/stripe/_handlers.py +18 -7
  363. package/server/nodes/stripe/_install.py +14 -15
  364. package/server/nodes/stripe/_source.py +5 -5
  365. package/server/nodes/stripe/icon.svg +1 -0
  366. package/server/nodes/stripe/meta.json +3 -0
  367. package/server/nodes/stripe/stripe_action.py +4 -4
  368. package/server/nodes/stripe/stripe_receive.py +6 -9
  369. package/server/nodes/telegram/__init__.py +13 -0
  370. package/server/nodes/telegram/_credentials.py +2 -7
  371. package/server/nodes/telegram/_events.py +167 -0
  372. package/server/nodes/telegram/_filters.py +3 -11
  373. package/server/nodes/telegram/_handlers.py +17 -7
  374. package/server/nodes/telegram/_refresh.py +24 -34
  375. package/server/nodes/telegram/_service.py +29 -45
  376. package/server/nodes/telegram/meta.json +3 -0
  377. package/server/nodes/telegram/telegram.svg +3 -0
  378. package/server/nodes/telegram/telegram_receive.py +38 -18
  379. package/server/nodes/telegram/telegram_send.py +21 -19
  380. package/server/nodes/text/{file_handler.py → file_handler/__init__.py} +7 -1
  381. package/server/nodes/text/file_handler/meta.json +3 -0
  382. package/server/nodes/text/{text_generator.py → text_generator/__init__.py} +2 -1
  383. package/server/nodes/text/text_generator/meta.json +3 -0
  384. package/server/nodes/tool/{agent_builder.py → agent_builder/__init__.py} +105 -100
  385. package/server/nodes/tool/agent_builder/_events.py +91 -0
  386. package/server/nodes/tool/agent_builder/meta.json +3 -0
  387. package/server/nodes/tool/{calculator_tool.py → calculator_tool/__init__.py} +19 -7
  388. package/server/nodes/tool/calculator_tool/meta.json +3 -0
  389. package/server/nodes/tool/{current_time_tool.py → current_time_tool/__init__.py} +6 -4
  390. package/server/nodes/tool/current_time_tool/meta.json +3 -0
  391. package/server/nodes/tool/{task_manager.py → task_manager/__init__.py} +17 -18
  392. package/server/nodes/tool/task_manager/meta.json +3 -0
  393. package/server/nodes/tool/{write_todos.py → write_todos/__init__.py} +20 -6
  394. package/server/nodes/tool/write_todos/meta.json +3 -0
  395. package/server/nodes/trigger/{chat_trigger.py → chat_trigger/__init__.py} +11 -7
  396. package/server/nodes/trigger/chat_trigger/_events.py +53 -0
  397. package/server/nodes/trigger/chat_trigger/meta.json +3 -0
  398. package/server/nodes/trigger/{task_trigger.py → task_trigger/__init__.py} +10 -7
  399. package/server/nodes/trigger/task_trigger/meta.json +3 -0
  400. package/server/nodes/trigger/{webhook_trigger.py → webhook_trigger/__init__.py} +10 -7
  401. package/server/nodes/trigger/webhook_trigger/_events.py +54 -0
  402. package/server/nodes/trigger/webhook_trigger/meta.json +3 -0
  403. package/server/nodes/twitter/__init__.py +7 -1
  404. package/server/nodes/twitter/_base.py +86 -61
  405. package/server/nodes/twitter/_credentials.py +7 -5
  406. package/server/nodes/twitter/_events.py +101 -0
  407. package/server/nodes/twitter/_filters.py +9 -9
  408. package/server/nodes/twitter/_handlers.py +3 -1
  409. package/server/nodes/twitter/_oauth.py +1 -2
  410. package/server/nodes/twitter/_refresh.py +8 -12
  411. package/server/nodes/twitter/{twitter_receive.py → twitter_receive/__init__.py} +7 -7
  412. package/server/nodes/twitter/twitter_receive/icon.svg +1 -0
  413. package/server/nodes/twitter/twitter_receive/meta.json +3 -0
  414. package/server/nodes/twitter/{twitter_search.py → twitter_search/__init__.py} +16 -11
  415. package/server/nodes/twitter/twitter_search/icon.svg +1 -0
  416. package/server/nodes/twitter/twitter_search/meta.json +3 -0
  417. package/server/nodes/twitter/{twitter_send.py → twitter_send/__init__.py} +60 -27
  418. package/server/nodes/twitter/twitter_send/icon.svg +1 -0
  419. package/server/nodes/twitter/twitter_send/meta.json +3 -0
  420. package/server/nodes/twitter/{twitter_user.py → twitter_user/__init__.py} +34 -19
  421. package/server/nodes/twitter/twitter_user/icon.svg +1 -0
  422. package/server/nodes/twitter/twitter_user/meta.json +3 -0
  423. package/server/nodes/utility/{console.py → console/__init__.py} +17 -22
  424. package/server/nodes/utility/console/meta.json +3 -0
  425. package/server/nodes/utility/{http_request.py → http_request/__init__.py} +9 -6
  426. package/server/nodes/utility/http_request/meta.json +3 -0
  427. package/server/nodes/utility/{process_manager.py → process_manager/__init__.py} +10 -6
  428. package/server/nodes/utility/process_manager/meta.json +3 -0
  429. package/server/nodes/utility/team_monitor/meta.json +3 -0
  430. package/server/nodes/utility/{webhook_response.py → webhook_response/__init__.py} +12 -7
  431. package/server/nodes/utility/webhook_response/meta.json +3 -0
  432. package/server/nodes/visuals.json +69 -251
  433. package/server/nodes/whatsapp/__init__.py +24 -0
  434. package/server/nodes/whatsapp/_base.py +283 -338
  435. package/server/nodes/whatsapp/_credentials.py +44 -0
  436. package/server/nodes/whatsapp/_events.py +277 -0
  437. package/server/nodes/whatsapp/_filters.py +36 -37
  438. package/server/nodes/whatsapp/_handlers.py +2 -0
  439. package/server/nodes/whatsapp/_option_loaders.py +1 -3
  440. package/server/nodes/whatsapp/_refresh.py +13 -18
  441. package/server/nodes/whatsapp/_runtime.py +9 -6
  442. package/server/nodes/whatsapp/_service.py +89 -152
  443. package/server/nodes/whatsapp/meta.json +3 -0
  444. package/server/nodes/whatsapp/whatsapp_db.py +116 -54
  445. package/server/nodes/whatsapp/whatsapp_receive.py +30 -13
  446. package/server/nodes/whatsapp/whatsapp_send.py +60 -37
  447. package/server/nodes/workflow/{start.py → start/__init__.py} +1 -4
  448. package/server/nodes/workflow/start/meta.json +3 -0
  449. package/server/package-lock.json +3 -3
  450. package/server/package.json +3 -0
  451. package/server/pyproject.toml +39 -10
  452. package/server/requirements.txt +3 -5
  453. package/server/routers/__init__.py +1 -1
  454. package/server/routers/auth.py +16 -56
  455. package/server/routers/database.py +27 -50
  456. package/server/routers/nodejs_compat.py +25 -87
  457. package/server/routers/schemas.py +66 -2
  458. package/server/routers/webhook.py +12 -12
  459. package/server/routers/websocket.py +312 -1716
  460. package/server/routers/workflow.py +28 -53
  461. package/server/scripts/smoke_test_skills.py +178 -0
  462. package/server/services/__init__.py +1 -1
  463. package/server/services/_supervisor/process.py +9 -3
  464. package/server/services/_supervisor/registry.py +3 -3
  465. package/server/services/_supervisor/util.py +1 -1
  466. package/server/services/agent_team.py +15 -43
  467. package/server/services/agent_teams/__init__.py +17 -0
  468. package/server/services/agent_teams/handlers.py +195 -0
  469. package/server/services/ai.py +853 -1108
  470. package/server/services/auth.py +10 -34
  471. package/server/services/chat_client.py +5 -34
  472. package/server/services/circuit_breaker.py +2 -6
  473. package/server/services/cli_agent/__init__.py +28 -4
  474. package/server/services/cli_agent/_cli_auth.py +61 -0
  475. package/server/services/cli_agent/_handlers.py +24 -183
  476. package/server/services/cli_agent/config.py +5 -8
  477. package/server/services/cli_agent/factory.py +168 -22
  478. package/server/services/cli_agent/jsonl_watcher.py +380 -0
  479. package/server/services/cli_agent/lockfile.py +9 -2
  480. package/server/services/cli_agent/mcp_server.py +110 -34
  481. package/server/services/cli_agent/protocol.py +37 -19
  482. package/server/services/cli_agent/providers/__init__.py +8 -4
  483. package/server/services/cli_agent/providers/google_gemini.py +11 -5
  484. package/server/services/cli_agent/providers/openai_codex.py +34 -34
  485. package/server/services/cli_agent/service.py +245 -83
  486. package/server/services/cli_agent/session.py +409 -229
  487. package/server/services/cli_agent/transports/__init__.py +47 -0
  488. package/server/services/cli_agent/transports/base.py +111 -0
  489. package/server/services/cli_agent/transports/posix.py +196 -0
  490. package/server/services/cli_agent/transports/windows.py +189 -0
  491. package/server/services/cli_agent/types.py +45 -18
  492. package/server/services/cli_agent/workflow_tools.py +28 -15
  493. package/server/services/compaction.py +68 -52
  494. package/server/services/credential_registry.py +6 -20
  495. package/server/services/credentials/__init__.py +18 -0
  496. package/server/services/credentials/handlers.py +196 -0
  497. package/server/services/deployment/__init__.py +12 -1
  498. package/server/services/deployment/canary_registry.py +137 -0
  499. package/server/services/deployment/handlers.py +382 -0
  500. package/server/services/deployment/manager.py +653 -163
  501. package/server/services/deployment/poll_registry.py +2 -6
  502. package/server/services/deployment/state.py +2 -0
  503. package/server/services/deployment/triggers.py +87 -93
  504. package/server/services/event_waiter.py +47 -54
  505. package/server/services/events/__init__.py +11 -0
  506. package/server/services/events/admin_handlers.py +368 -0
  507. package/server/services/events/daemon.py +3 -1
  508. package/server/services/events/dispatch.py +188 -0
  509. package/server/services/events/envelope.py +264 -45
  510. package/server/services/events/oauth_lifecycle.py +98 -42
  511. package/server/services/events/triggers.py +3 -13
  512. package/server/services/events/verifiers/hmac_basic.py +1 -1
  513. package/server/services/events/verifiers/standard_webhooks.py +2 -4
  514. package/server/services/events/webhook.py +2 -3
  515. package/server/services/example_loader.py +73 -15
  516. package/server/services/execution/cache.py +36 -76
  517. package/server/services/execution/conditions.py +7 -20
  518. package/server/services/execution/dlq.py +20 -24
  519. package/server/services/execution/executor.py +234 -265
  520. package/server/services/execution/models.py +40 -46
  521. package/server/services/execution/recovery.py +23 -46
  522. package/server/services/handlers/__init__.py +12 -16
  523. package/server/services/handlers/todo.py +3 -6
  524. package/server/services/handlers/tools.py +143 -194
  525. package/server/services/handlers/triggers.py +24 -23
  526. package/server/services/llm/config.py +10 -1
  527. package/server/services/llm/factory.py +16 -4
  528. package/server/services/llm/messages.py +1 -5
  529. package/server/services/llm/protocol.py +9 -1
  530. package/server/services/llm/providers/anthropic.py +23 -12
  531. package/server/services/llm/providers/gemini.py +43 -22
  532. package/server/services/llm/providers/openai.py +14 -6
  533. package/server/services/llm/providers/openrouter.py +6 -1
  534. package/server/services/markdown_formatter.py +1 -2
  535. package/server/services/memory/__init__.py +2 -2
  536. package/server/services/memory/jsonl.py +6 -2
  537. package/server/services/memory/markdown.py +6 -6
  538. package/server/services/memory/state.py +6 -5
  539. package/server/services/memory_store.py +8 -12
  540. package/server/services/model_registry.py +22 -20
  541. package/server/services/node_executor.py +85 -80
  542. package/server/services/node_output_schemas.py +4 -7
  543. package/server/services/node_registry.py +40 -4
  544. package/server/services/node_spec.py +3 -7
  545. package/server/services/nodejs_client.py +4 -14
  546. package/server/services/oauth_utils.py +11 -7
  547. package/server/services/parameter_resolver.py +30 -36
  548. package/server/services/plugin/base.py +321 -38
  549. package/server/services/plugin/connection.py +12 -7
  550. package/server/services/plugin/credential.py +80 -22
  551. package/server/services/plugin/edge_walker.py +128 -105
  552. package/server/services/plugin/identifiers.py +48 -0
  553. package/server/services/plugin/interceptor.py +1 -1
  554. package/server/services/plugin/oauth.py +25 -21
  555. package/server/services/plugin/operation.py +1 -1
  556. package/server/services/plugin/polling.py +151 -26
  557. package/server/services/plugin/registry.py +52 -4
  558. package/server/services/plugin/routing.py +6 -9
  559. package/server/services/plugin/scaling.py +36 -18
  560. package/server/services/plugin/service_factories.py +95 -0
  561. package/server/services/plugin/shutdown_hooks.py +103 -0
  562. package/server/services/plugin/social_provider_registry.py +80 -0
  563. package/server/services/plugin/ws.py +2 -1
  564. package/server/services/pricing.py +26 -40
  565. package/server/services/pricing_handlers.py +90 -0
  566. package/server/services/process_service.py +33 -32
  567. package/server/services/proxy/models.py +15 -9
  568. package/server/services/proxy/service.py +26 -40
  569. package/server/services/rlm/adapters.py +43 -40
  570. package/server/services/rlm/constants.py +9 -9
  571. package/server/services/rlm/service.py +57 -45
  572. package/server/services/scheduler.py +8 -39
  573. package/server/services/settings/__init__.py +16 -0
  574. package/server/services/settings/handlers.py +275 -0
  575. package/server/services/skill_loader.py +53 -45
  576. package/server/services/skill_prompt.py +8 -6
  577. package/server/services/skills/__init__.py +23 -0
  578. package/server/services/skills/handlers.py +479 -0
  579. package/server/services/status_broadcaster.py +314 -291
  580. package/server/services/temporal/__init__.py +22 -1
  581. package/server/services/temporal/_handlers.py +65 -0
  582. package/server/services/temporal/_install.py +158 -0
  583. package/server/services/temporal/_refresh.py +57 -0
  584. package/server/services/temporal/_retry_policies.py +85 -0
  585. package/server/services/temporal/_runtime.py +181 -0
  586. package/server/services/temporal/_supervised_runtime.py +102 -0
  587. package/server/services/temporal/activities.py +168 -11
  588. package/server/services/temporal/agent_activities.py +683 -0
  589. package/server/services/temporal/agent_workflow.py +601 -0
  590. package/server/services/temporal/client.py +58 -13
  591. package/server/services/temporal/executor.py +2 -3
  592. package/server/services/temporal/plugin_activities.py +37 -2
  593. package/server/services/temporal/plugin_registry.py +82 -0
  594. package/server/services/temporal/polling_trigger_workflow.py +267 -0
  595. package/server/services/temporal/schedules.py +220 -0
  596. package/server/services/temporal/search_attributes.py +177 -0
  597. package/server/services/temporal/trigger_listener_workflow.py +378 -0
  598. package/server/services/temporal/worker.py +111 -18
  599. package/server/services/temporal/workflow.py +259 -40
  600. package/server/services/temporal/ws_client.py +22 -11
  601. package/server/services/text.py +14 -28
  602. package/server/services/tracked_http.py +29 -49
  603. package/server/services/user_auth.py +7 -21
  604. package/server/services/workflow.py +28 -20
  605. package/server/services/workflow_import.py +351 -0
  606. package/server/services/workflow_ops.py +4 -0
  607. package/server/services/workflow_storage/__init__.py +18 -0
  608. package/server/services/workflow_storage/handlers.py +132 -0
  609. package/server/services/workflow_validator.py +209 -0
  610. package/server/services/ws_handler_registry.py +80 -9
  611. package/server/skills/assistant/agent-builder-skill/SKILL.md +6 -6
  612. package/server/tests/conftest.py +54 -3
  613. package/server/tests/credentials/test_auth_service.py +9 -21
  614. package/server/tests/credentials/test_credential_broadcasts.py +116 -22
  615. package/server/tests/credentials/test_credentials_database.py +12 -38
  616. package/server/tests/credentials/test_encryption.py +3 -9
  617. package/server/tests/credentials/test_google_oauth.py +1 -3
  618. package/server/tests/credentials/test_oauth_utils.py +31 -38
  619. package/server/tests/credentials/test_twitter_oauth.py +1 -3
  620. package/server/tests/credentials/test_websocket_handlers.py +37 -72
  621. package/server/tests/fixtures/tool_names_snapshot.json +78 -0
  622. package/server/tests/llm/test_factory.py +12 -4
  623. package/server/tests/llm/test_providers.py +25 -32
  624. package/server/tests/llm/test_wiring.py +27 -22
  625. package/server/tests/nodes/_compat.py +4 -5
  626. package/server/tests/nodes/_harness.py +31 -24
  627. package/server/tests/nodes/_mocks.py +2 -6
  628. package/server/tests/nodes/test_agent_builder.py +43 -35
  629. package/server/tests/nodes/test_ai_agents.py +29 -24
  630. package/server/tests/nodes/test_ai_chat_models.py +3 -9
  631. package/server/tests/nodes/test_ai_tools.py +29 -24
  632. package/server/tests/nodes/test_android.py +34 -64
  633. package/server/tests/nodes/test_chat_utility.py +2 -2
  634. package/server/tests/nodes/test_code_fs_process.py +26 -84
  635. package/server/tests/nodes/test_document.py +23 -47
  636. package/server/tests/nodes/test_email.py +88 -51
  637. package/server/tests/nodes/test_google_workspace.py +26 -20
  638. package/server/tests/nodes/test_http_proxy.py +43 -89
  639. package/server/tests/nodes/test_search.py +3 -9
  640. package/server/tests/nodes/test_specialized_agents.py +58 -162
  641. package/server/tests/nodes/test_stripe_plugin.py +25 -5
  642. package/server/tests/nodes/test_telegram_social.py +33 -37
  643. package/server/tests/nodes/test_twitter.py +59 -150
  644. package/server/tests/nodes/test_web_automation.py +21 -51
  645. package/server/tests/nodes/test_whatsapp.py +13 -19
  646. package/server/tests/nodes/test_workflow_triggers.py +16 -45
  647. package/server/tests/services/cli_agent/test_claude_session_events.py +201 -0
  648. package/server/tests/services/cli_agent/test_jsonl_watcher.py +190 -0
  649. package/server/tests/services/cli_agent/test_mcp_server.py +67 -29
  650. package/server/tests/services/cli_agent/test_providers.py +236 -47
  651. package/server/tests/services/cli_agent/test_service.py +9 -7
  652. package/server/tests/services/memory/test_jsonl.py +30 -25
  653. package/server/tests/services/test_events.py +26 -7
  654. package/server/tests/services/test_identifiers.py +122 -0
  655. package/server/tests/services/test_process_lifecycle.py +129 -0
  656. package/server/tests/services/test_supervisor.py +0 -1
  657. package/server/tests/temporal/__init__.py +0 -0
  658. package/server/tests/temporal/test_agent_workflow.py +215 -0
  659. package/server/tests/temporal/test_dispatch.py +231 -0
  660. package/server/tests/test_admin_handlers.py +394 -0
  661. package/server/tests/test_auto_skill.py +4 -2
  662. package/server/tests/test_canary_registry.py +310 -0
  663. package/server/tests/test_chat_trigger_canary_producer.py +101 -0
  664. package/server/tests/test_cloudevents_node_parameters.py +129 -0
  665. package/server/tests/test_credential_icon.py +115 -0
  666. package/server/tests/test_cron_canary.py +511 -0
  667. package/server/tests/test_deployment_canary_listener.py +692 -0
  668. package/server/tests/test_event_framework_phase_a.py +537 -0
  669. package/server/tests/test_no_raw_prints.py +131 -0
  670. package/server/tests/test_node_spec.py +196 -103
  671. package/server/tests/test_parameter_resolver.py +20 -20
  672. package/server/tests/test_plugin_contract.py +76 -49
  673. package/server/tests/test_plugin_helpers.py +0 -1
  674. package/server/tests/test_plugin_self_containment.py +40 -47
  675. package/server/tests/test_polling_trigger_workflow.py +572 -0
  676. package/server/tests/test_retry_policies.py +146 -0
  677. package/server/tests/test_service_factories.py +168 -0
  678. package/server/tests/test_shutdown_hooks.py +199 -0
  679. package/server/tests/test_social_provider_registry.py +177 -0
  680. package/server/tests/test_status_broadcasts.py +214 -63
  681. package/server/tests/test_task_trigger_canary_producer.py +131 -0
  682. package/server/tests/test_telegram_trigger_canary_producer.py +113 -0
  683. package/server/tests/test_tool_registry.py +110 -0
  684. package/server/tests/test_trigger_listener_workflow.py +365 -0
  685. package/server/tests/test_whatsapp_trigger_canary_producer.py +164 -0
  686. package/server/tests/test_workflow_ops.py +1 -3
  687. package/server/tests/test_workflow_validator.py +791 -0
  688. package/server/uv.lock +3539 -0
  689. package/client/dist/assets/index-DQ0nwhec.js +0 -257
  690. package/client/src/assets/icons/apify/index.ts +0 -19
  691. package/client/src/assets/icons/browser/index.ts +0 -17
  692. package/client/src/assets/icons/email/index.ts +0 -22
  693. package/client/src/assets/icons/google/index.ts +0 -34
  694. package/client/src/assets/icons/llm/deepseek.svg +0 -1
  695. package/client/src/assets/icons/llm/index.ts +0 -18
  696. package/client/src/assets/icons/llm/kimi.svg +0 -1
  697. package/client/src/assets/icons/llm/mistral.svg +0 -1
  698. package/client/src/assets/icons/search/index.ts +0 -28
  699. package/client/src/assets/icons/telegram/index.ts +0 -19
  700. package/machina/buildenv.py +0 -44
  701. package/machina/cli.py +0 -55
  702. package/machina/commands/__init__.py +0 -1
  703. package/machina/commands/clean.py +0 -80
  704. package/machina/commands/daemon.py +0 -150
  705. package/machina/config.py +0 -93
  706. package/machina/platform_.py +0 -37
  707. package/machina/pyproject.toml +0 -33
  708. package/server/nodes/agent/deep_agent.py +0 -103
  709. package/server/services/agents/__init__.py +0 -9
  710. package/server/services/agents/adapters.py +0 -199
  711. package/server/services/agents/constants.py +0 -10
  712. package/server/services/agents/service.py +0 -297
  713. package/server/services/cli_agent/providers/anthropic_claude.py +0 -419
  714. /package/{machina → cli}/README.md +0 -0
  715. /package/{machina → cli}/__init__.py +0 -0
  716. /package/{client/src/assets/icons/apify → server/credentials/icons}/apify.svg +0 -0
  717. /package/{client/src/assets/icons/search/brave.svg → server/credentials/icons/brave_search.svg} +0 -0
  718. /package/{client/src/assets/icons/email/read.svg → server/credentials/icons/email_himalaya.svg} +0 -0
  719. /package/{client/src/assets/icons/search → server/credentials/icons}/perplexity.svg +0 -0
  720. /package/{client/src/assets/icons/search/google.svg → server/credentials/icons/serper.svg} +0 -0
  721. /package/{client/src/assets → server/credentials}/icons/stripe.svg +0 -0
  722. /package/{client/src/assets/icons/twitter/x.svg → server/credentials/icons/twitter.svg} +0 -0
  723. /package/{client/src/assets/icons/browser/chrome.svg → server/nodes/browser/browser/icon.svg} +0 -0
  724. /package/{client/src/assets/icons/chat/chat.svg → server/nodes/chat/chat_history/icon.svg} +0 -0
  725. /package/{client/src/assets/icons/code/javascript.svg → server/nodes/code/javascript_executor/icon.svg} +0 -0
  726. /package/{client/src/assets/icons/code/python.svg → server/nodes/code/python_executor/icon.svg} +0 -0
  727. /package/{client/src/assets/icons/code/typescript.svg → server/nodes/code/typescript_executor/icon.svg} +0 -0
  728. /package/{client/src/assets/icons/email/receive.svg → server/nodes/email/email_receive/icon.svg} +0 -0
  729. /package/{client/src/assets/icons/email/send.svg → server/nodes/email/email_send/icon.svg} +0 -0
  730. /package/{client/src/assets/icons/google/calendar.svg → server/nodes/google/calendar/icon.svg} +0 -0
  731. /package/{client/src/assets/icons/google/contacts.svg → server/nodes/google/contacts/icon.svg} +0 -0
  732. /package/{client/src/assets/icons/google/drive.svg → server/nodes/google/drive/icon.svg} +0 -0
  733. /package/{client/src/assets/icons/google/gmail.svg → server/nodes/google/gmail/icon.svg} +0 -0
  734. /package/{client/src/assets/icons/google/sheets.svg → server/nodes/google/sheets/icon.svg} +0 -0
  735. /package/{client/src/assets/icons/google/tasks.svg → server/nodes/google/tasks/icon.svg} +0 -0
  736. /package/{client/src/assets/icons/search/duckduckgo.svg → server/nodes/search/duckduckgo_search/icon.svg} +0 -0
  737. /package/{client/src/assets/icons/social/social.svg → server/nodes/social/social_receive/icon.svg} +0 -0
  738. /package/{client/src/assets/icons/telegram/telegram.svg → server/nodes/telegram/icon.svg} +0 -0
  739. /package/server/nodes/utility/{team_monitor.py → team_monitor/__init__.py} +0 -0
  740. /package/{client/src/assets/icons/whatsapp/whatsapp-db.svg → server/nodes/whatsapp/icon_whatsappDb.svg} +0 -0
  741. /package/{client/src/assets/icons/whatsapp/whatsapp-receive.svg → server/nodes/whatsapp/icon_whatsappReceive.svg} +0 -0
  742. /package/{client/src/assets/icons/whatsapp/whatsapp-send.svg → server/nodes/whatsapp/icon_whatsappSend.svg} +0 -0
  743. /package/{client/src/assets/icons → server/nodes}/whatsapp/whatsapp.svg +0 -0
@@ -1,12 +1,30 @@
1
- """Factory for `AICliProvider` instances.
1
+ """Registry + factory for `AICliProvider` instances.
2
2
 
3
- Mirrors `services/llm/factory.py`: lazy imports keep optional providers
4
- from being loaded until first use. v1 ships Claude + Codex; calling
5
- `create_cli_provider("gemini")` raises `NotImplementedError` (v2).
3
+ Plugins call :func:`register_provider` from their package
4
+ ``__init__.py`` so the framework can build an instance by provider
5
+ name without importing from the plugin folder directly (which would
6
+ be a ``services → nodes`` layering violation — forbidden by the
7
+ plugin-folder pattern in
8
+ [docs-internal/plugin_system.md](../../../../docs-internal/plugin_system.md)).
9
+
10
+ The pattern matches the six existing per-plugin registries
11
+ (``ws_handler_registry``, ``register_router``, etc.) — see
12
+ :func:`services.ws_handler_registry.register_ws_handlers` for the
13
+ template. Provider registration is idempotent: a second
14
+ ``register_provider("claude", AnthropicClaudeProvider)`` is a no-op
15
+ when the binding already matches; mismatching bindings raise.
16
+
17
+ Usage from a plugin's ``__init__.py``::
18
+
19
+ from services.cli_agent.factory import register_provider
20
+ from ._provider import AnthropicClaudeProvider
21
+ register_provider("claude", AnthropicClaudeProvider)
6
22
  """
7
23
 
8
24
  from __future__ import annotations
9
25
 
26
+ from typing import Any, Callable, Dict, Optional, Type, Union
27
+
10
28
  from core.logging import get_logger
11
29
 
12
30
  from services.cli_agent.protocol import AICliProvider
@@ -14,35 +32,163 @@ from services.cli_agent.protocol import AICliProvider
14
32
  logger = get_logger(__name__)
15
33
 
16
34
 
17
- SUPPORTED_PROVIDERS = frozenset({"claude", "codex"}) # v2 adds "gemini"
18
- ALL_PROVIDERS = frozenset({"claude", "codex", "gemini"}) # union of v1+v2 names
35
+ # Bindings populated by plugin folders on import. ``Union[Type, Callable]``
36
+ # because some plugins may want a zero-arg factory function instead of a
37
+ # class — both shapes are valid as long as calling them returns an
38
+ # ``AICliProvider``-conformant instance.
39
+ _PROVIDER_REGISTRY: Dict[str, Union[Type[AICliProvider], Callable[[], AICliProvider]]] = {}
40
+
41
+ # Per-provider session pool factories. Pool implementations are
42
+ # provider-specific (claude uses subprocess + stream-json pooling; codex
43
+ # / gemini don't) and live in the plugin folder per the canonical
44
+ # layout. This registry lets the framework's pool dispatcher
45
+ # (``service.py::_run_pooled_turn``) look up the right pool by
46
+ # provider name without importing from the plugin folder directly.
47
+ _POOL_REGISTRY: Dict[str, Callable[[], Any]] = {}
48
+
49
+ # Per-provider skill materialisers. Only claude implements one today —
50
+ # it writes ``SKILL.md`` trees under ``<workspace>/.claude/skills/``
51
+ # per the Anthropic Skills spec (code.claude.com/docs/en/skills).
52
+ # Codex / Gemini don't honor SKILL.md so they don't register here.
53
+ # The framework's session bootstrap (``session.py:_pre_spawn``)
54
+ # looks up by provider name; ``None`` means no-op (skip materialise).
55
+ #
56
+ # Signature: ``async (workspace_dir, skill_names, *, previous_skill_names,
57
+ # log_label) -> tuple[int, int]`` — (added_count, removed_count).
58
+ _SKILL_MATERIALISER_REGISTRY: Dict[str, Callable[..., Any]] = {}
59
+
60
+
61
+ def register_provider(
62
+ name: str,
63
+ provider_factory: Union[Type[AICliProvider], Callable[[], AICliProvider]],
64
+ ) -> None:
65
+ """Bind a provider name to its class / factory.
66
+
67
+ Called from the plugin folder's ``__init__.py`` on import. Idempotent
68
+ when the binding is unchanged; raises ``ValueError`` on conflict so
69
+ accidental double-registration with different classes is caught
70
+ instead of silently shadowing.
71
+ """
72
+ existing = _PROVIDER_REGISTRY.get(name)
73
+ if existing is not None and existing is not provider_factory:
74
+ raise ValueError(f"CLI provider {name!r} already registered with {existing!r}; " f"refusing to overwrite with {provider_factory!r}")
75
+ _PROVIDER_REGISTRY[name] = provider_factory
76
+ logger.debug("[cli_agent] registered provider %r -> %r", name, provider_factory)
77
+
78
+
79
+ def unregister_provider(name: str) -> None:
80
+ """Drop a provider binding. Used by tests."""
81
+ _PROVIDER_REGISTRY.pop(name, None)
19
82
 
20
83
 
21
84
  def create_cli_provider(name: str) -> AICliProvider:
22
85
  """Build a CLI provider by name.
23
86
 
24
87
  Raises:
25
- NotImplementedError: for `gemini` until the v2 implementation lands.
88
+ NotImplementedError: for declared-but-deferred providers
89
+ (currently ``gemini``).
26
90
  ValueError: for unknown names.
27
91
  """
28
- if name == "claude":
29
- from services.cli_agent.providers.anthropic_claude import AnthropicClaudeProvider
30
- return AnthropicClaudeProvider()
31
-
32
- if name == "codex":
33
- from services.cli_agent.providers.openai_codex import OpenAICodexProvider
34
- return OpenAICodexProvider()
92
+ factory = _PROVIDER_REGISTRY.get(name)
93
+ if factory is not None:
94
+ return factory()
35
95
 
96
+ # ``gemini`` is intentionally listed as a known-but-unsupported name
97
+ # so the frontend dropdown can show it greyed-out while the v2
98
+ # implementation is in flight. Surface a clean ``NotImplementedError``
99
+ # so factory consumers can detect the deferred state.
36
100
  if name == "gemini":
37
- # v2 stub surface a clean error so factory consumers can detect
38
- # the deferred state and offer the user an actionable hint.
39
- raise NotImplementedError(
40
- "gemini provider deferred to v2. Use 'claude' or 'codex' in v1."
41
- )
101
+ raise NotImplementedError("gemini provider deferred to v2. Use 'claude' or 'codex' in v1.")
42
102
 
43
- raise ValueError(f"Unknown CLI provider: {name!r}. Known: {sorted(ALL_PROVIDERS)}")
103
+ registered = sorted(_PROVIDER_REGISTRY.keys())
104
+ raise ValueError(
105
+ f"Unknown CLI provider: {name!r}. " f"Registered: {registered}. " f"Did the plugin's ``__init__.py`` call register_provider()?"
106
+ )
44
107
 
45
108
 
46
109
  def is_supported(name: str) -> bool:
47
- """True if the provider is fully implemented in this version."""
48
- return name in SUPPORTED_PROVIDERS
110
+ """True if the provider is registered (plugin imported successfully)."""
111
+ return name in _PROVIDER_REGISTRY
112
+
113
+
114
+ def registered_provider_names() -> frozenset[str]:
115
+ """Frozen snapshot of currently-registered provider names."""
116
+ return frozenset(_PROVIDER_REGISTRY.keys())
117
+
118
+
119
+ # ---------------------------------------------------------------------------
120
+ # Session-pool registry — paired with provider registry above
121
+ # ---------------------------------------------------------------------------
122
+
123
+
124
+ def register_session_pool(
125
+ provider_name: str,
126
+ pool_getter: Callable[[], Any],
127
+ ) -> None:
128
+ """Bind a provider to a callable returning its session pool singleton.
129
+
130
+ Called from the plugin folder's ``__init__.py`` on import. The
131
+ pool object must expose ``acquire``, ``send_turn``, ``release``,
132
+ ``clear``, ``terminate``, ``shutdown_all``, and ``start_reaper`` —
133
+ the surface ``services.cli_agent.service._run_pooled_turn`` uses.
134
+
135
+ Idempotent on identical binding; raises ``ValueError`` on conflict
136
+ so accidental double-registration with different getters is caught.
137
+ """
138
+ existing = _POOL_REGISTRY.get(provider_name)
139
+ if existing is not None and existing is not pool_getter:
140
+ raise ValueError(
141
+ f"Session pool for {provider_name!r} already registered " f"({existing!r}); refusing to overwrite with {pool_getter!r}"
142
+ )
143
+ _POOL_REGISTRY[provider_name] = pool_getter
144
+ logger.debug(
145
+ "[cli_agent] registered session pool %r -> %r",
146
+ provider_name,
147
+ pool_getter,
148
+ )
149
+
150
+
151
+ def get_session_pool(provider_name: str) -> Optional[Any]:
152
+ """Return the session pool singleton for ``provider_name`` or ``None``.
153
+
154
+ Calls the registered getter (provider-side) so the pool is lazily
155
+ instantiated on first request.
156
+ """
157
+ getter = _POOL_REGISTRY.get(provider_name)
158
+ if getter is None:
159
+ return None
160
+ return getter()
161
+
162
+
163
+ # ---------------------------------------------------------------------------
164
+ # Skill-materialiser registry
165
+ # ---------------------------------------------------------------------------
166
+
167
+
168
+ def register_skill_materialiser(
169
+ provider_name: str,
170
+ materialiser: Callable[..., Any],
171
+ ) -> None:
172
+ """Bind a provider to its skill materialiser (claude only today).
173
+
174
+ Called from the plugin folder's ``__init__.py`` on import. The
175
+ materialiser must be an async callable with signature
176
+ ``(workspace_dir, skill_names, *, previous_skill_names, log_label)
177
+ -> tuple[int, int]`` (added_count, removed_count).
178
+ """
179
+ existing = _SKILL_MATERIALISER_REGISTRY.get(provider_name)
180
+ if existing is not None and existing is not materialiser:
181
+ raise ValueError(
182
+ f"Skill materialiser for {provider_name!r} already registered " f"({existing!r}); refusing to overwrite with {materialiser!r}"
183
+ )
184
+ _SKILL_MATERIALISER_REGISTRY[provider_name] = materialiser
185
+ logger.debug(
186
+ "[cli_agent] registered skill materialiser %r -> %r",
187
+ provider_name,
188
+ materialiser,
189
+ )
190
+
191
+
192
+ def get_skill_materialiser(provider_name: str) -> Optional[Callable[..., Any]]:
193
+ """Return the registered materialiser or ``None`` (= skip materialise)."""
194
+ return _SKILL_MATERIALISER_REGISTRY.get(provider_name)
@@ -0,0 +1,380 @@
1
+ """On-disk JSONL watchers — protocol surface for interactive-mode claude.
2
+
3
+ Claude writes its session transcript to
4
+ ``<CLAUDE_CONFIG_DIR>/projects/<project_key>/<session_uuid>.jsonl`` —
5
+ same shape ``-p`` used to write to stdout (Claude Code CHANGELOG
6
+ 2.1.101 / 2.1.126 confirms the shared writer). In interactive mode we
7
+ keep the TUI alive in a PTY and read events off disk; this module
8
+ provides the two watchers ``AICliSession`` + ``ClaudeSessionPool`` need:
9
+
10
+ - :class:`JsonlWatcher` tails one specific JSONL file and dispatches
11
+ each new line as a parsed event. Drop-in replacement for the old
12
+ ``_consume_stdout`` NDJSON parser.
13
+
14
+ - :class:`JsonlDirWatcher` watches a directory for *new* JSONL files
15
+ appearing — the mechanism the session pool uses to capture the
16
+ new session UUID after sending ``/clear`` (which mints a fresh
17
+ UUID, not an in-place clear; see issue `claude-code#32871
18
+ <https://github.com/anthropics/claude-code/issues/32871>`_).
19
+
20
+ Both use a simple poll loop (default 100 ms / 250 ms) rather than
21
+ ``watchdog``'s OS-native APIs (inotify/FSEvents/ReadDirectoryChangesW).
22
+ Polling is good enough for our latency target (UI updates were already
23
+ at the same cadence under the old stdout path), more predictable
24
+ under high-frequency append, and avoids the well-known watchdog races
25
+ on Windows under rapid file creation.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import asyncio
31
+ import json
32
+ import os
33
+ from pathlib import Path
34
+ from typing import Any, Awaitable, Callable, Dict, Optional, Set
35
+
36
+ from core.logging import get_logger
37
+
38
+ logger = get_logger(__name__)
39
+
40
+
41
+ EventHandler = Callable[[Dict[str, Any]], Awaitable[None]]
42
+ """Async callback fired once per parsed JSONL line."""
43
+
44
+ FileHandler = Callable[[Path], Awaitable[None]]
45
+ """Async callback fired once per newly-detected .jsonl file."""
46
+
47
+
48
+ class JsonlWatcher:
49
+ """Tail a specific JSONL file and dispatch new lines as events.
50
+
51
+ Replaces the old NDJSON-on-stdout consumer. Once :meth:`start` is
52
+ called, every newline-terminated chunk appended to ``path`` is read,
53
+ parsed with :func:`json.loads`, and handed to ``on_event``. Garbage
54
+ (unparseable lines, partial writes during an append) is silently
55
+ dropped — matches the old ``AnthropicClaudeProvider.parse_event``
56
+ return-None-on-error semantics.
57
+
58
+ The watcher is cancellation-safe: :meth:`stop` cancels the
59
+ background task and closes the open file handle. Calling
60
+ :meth:`stop` on an already-stopped watcher is a no-op.
61
+
62
+ Initial position: by default we seek to the END of the file at
63
+ :meth:`start` so we only see new appends. Pass ``start_from_end=False``
64
+ to replay the entire file (useful for picking up the result event
65
+ of a turn that completed while we were spawning).
66
+ """
67
+
68
+ __slots__ = (
69
+ "_path",
70
+ "_on_event",
71
+ "_poll_interval",
72
+ "_start_from_end",
73
+ "_task",
74
+ "_stopped",
75
+ )
76
+
77
+ def __init__(
78
+ self,
79
+ path: Path,
80
+ on_event: EventHandler,
81
+ *,
82
+ poll_interval: float = 0.1,
83
+ start_from_end: bool = False,
84
+ ) -> None:
85
+ self._path = Path(path)
86
+ self._on_event = on_event
87
+ self._poll_interval = max(0.01, float(poll_interval))
88
+ self._start_from_end = bool(start_from_end)
89
+ self._task: Optional[asyncio.Task[None]] = None
90
+ self._stopped = asyncio.Event()
91
+
92
+ @property
93
+ def path(self) -> Path:
94
+ return self._path
95
+
96
+ async def start(self) -> None:
97
+ """Spawn the tail-f task. Returns immediately."""
98
+ if self._task is not None and not self._task.done():
99
+ return # idempotent
100
+ self._stopped.clear()
101
+ self._task = asyncio.create_task(
102
+ self._run(),
103
+ name=f"JsonlWatcher({self._path.name})",
104
+ )
105
+
106
+ async def stop(self) -> None:
107
+ """Cancel the tail-f task and wait for it to settle."""
108
+ self._stopped.set()
109
+ if self._task is None:
110
+ return
111
+ if not self._task.done():
112
+ self._task.cancel()
113
+ try:
114
+ await self._task
115
+ except asyncio.CancelledError:
116
+ pass
117
+ except Exception as exc: # pragma: no cover — defensive
118
+ logger.debug(
119
+ "[JsonlWatcher] task exit error path=%s exc=%s",
120
+ self._path.name,
121
+ exc,
122
+ )
123
+ self._task = None
124
+
125
+ async def _run(self) -> None:
126
+ # Wait for the file to appear. Claude writes the JSONL on first
127
+ # turn output; for first-spawn runs we may start watching
128
+ # before it exists.
129
+ while not self._path.exists():
130
+ if self._stopped.is_set():
131
+ return
132
+ await asyncio.sleep(self._poll_interval)
133
+
134
+ # Open in binary mode so partial UTF-8 sequences at the read
135
+ # boundary don't blow up — we accumulate bytes and decode on
136
+ # the newline split.
137
+ try:
138
+ handle = self._path.open("rb")
139
+ except OSError as exc:
140
+ logger.warning(
141
+ "[JsonlWatcher] open failed path=%s exc=%s",
142
+ self._path,
143
+ exc,
144
+ )
145
+ return
146
+
147
+ buf = b""
148
+ try:
149
+ if self._start_from_end:
150
+ handle.seek(0, os.SEEK_END)
151
+
152
+ while not self._stopped.is_set():
153
+ chunk = handle.read()
154
+ if chunk:
155
+ buf += chunk
156
+ while b"\n" in buf:
157
+ raw, buf = buf.split(b"\n", 1)
158
+ await self._dispatch_line(raw)
159
+ else:
160
+ # No new data — sleep and try again. Use a small
161
+ # interval so latency stays comparable to stdout
162
+ # streaming.
163
+ try:
164
+ await asyncio.wait_for(
165
+ self._stopped.wait(),
166
+ timeout=self._poll_interval,
167
+ )
168
+ except asyncio.TimeoutError:
169
+ pass
170
+ except asyncio.CancelledError:
171
+ raise
172
+ except Exception as exc: # pragma: no cover — defensive
173
+ logger.warning(
174
+ "[JsonlWatcher] read loop ended unexpectedly path=%s exc=%s",
175
+ self._path.name,
176
+ exc,
177
+ )
178
+ finally:
179
+ # Flush any final no-newline bytes (claude generally writes
180
+ # newline-terminated lines but be defensive).
181
+ if buf.strip():
182
+ await self._dispatch_line(buf)
183
+ try:
184
+ handle.close()
185
+ except OSError:
186
+ pass
187
+
188
+ async def _dispatch_line(self, raw: bytes) -> None:
189
+ text = raw.decode("utf-8", errors="replace").strip()
190
+ if not text:
191
+ return
192
+ try:
193
+ event: Dict[str, Any] = json.loads(text)
194
+ except json.JSONDecodeError:
195
+ return # garbage / partial line — drop silently
196
+ try:
197
+ await self._on_event(event)
198
+ except asyncio.CancelledError:
199
+ raise
200
+ except Exception as exc: # pragma: no cover — handler isolation
201
+ logger.warning(
202
+ "[JsonlWatcher] on_event handler raised path=%s exc=%s",
203
+ self._path.name,
204
+ exc,
205
+ )
206
+
207
+
208
+ class JsonlDirWatcher:
209
+ """Watch a directory for newly-appearing ``.jsonl`` files.
210
+
211
+ Used by :class:`nodes.agent.claude_code_agent._pool.ClaudeSessionPool`
212
+ to capture the new session UUID after sending ``/clear``: claude
213
+ mints a fresh UUID and starts writing to
214
+ ``<project_key>/<new_uuid>.jsonl``; the watcher fires the callback
215
+ with that path, the pool reads the UUID off the filename, and
216
+ points its memory bridge at the new file.
217
+
218
+ Detection is "new file" only — we don't fire on appends to
219
+ existing files (that's :class:`JsonlWatcher`'s job). Files present
220
+ at :meth:`start` time are recorded as baseline and never fire the
221
+ callback.
222
+ """
223
+
224
+ __slots__ = (
225
+ "_dir",
226
+ "_on_new_file",
227
+ "_poll_interval",
228
+ "_task",
229
+ "_stopped",
230
+ "_baseline",
231
+ )
232
+
233
+ def __init__(
234
+ self,
235
+ directory: Path,
236
+ on_new_file: FileHandler,
237
+ *,
238
+ poll_interval: float = 0.25,
239
+ ) -> None:
240
+ self._dir = Path(directory)
241
+ self._on_new_file = on_new_file
242
+ self._poll_interval = max(0.05, float(poll_interval))
243
+ self._task: Optional[asyncio.Task[None]] = None
244
+ self._stopped = asyncio.Event()
245
+ self._baseline: Set[str] = set()
246
+
247
+ @property
248
+ def directory(self) -> Path:
249
+ return self._dir
250
+
251
+ async def start(self) -> None:
252
+ """Snapshot the current ``.jsonl`` files as the baseline, then
253
+ start polling. Files in the baseline never fire the callback."""
254
+ if self._task is not None and not self._task.done():
255
+ return
256
+ self._stopped.clear()
257
+ # Refresh baseline at every start so a re-used watcher across
258
+ # session-pool acquires sees a fresh snapshot.
259
+ self._baseline = self._snapshot()
260
+ self._task = asyncio.create_task(
261
+ self._run(),
262
+ name=f"JsonlDirWatcher({self._dir.name})",
263
+ )
264
+
265
+ async def stop(self) -> None:
266
+ self._stopped.set()
267
+ if self._task is None:
268
+ return
269
+ if not self._task.done():
270
+ self._task.cancel()
271
+ try:
272
+ await self._task
273
+ except asyncio.CancelledError:
274
+ pass
275
+ self._task = None
276
+
277
+ def _snapshot(self) -> Set[str]:
278
+ """Return the set of ``.jsonl`` filenames currently in the
279
+ directory. Empty set if the directory doesn't exist yet."""
280
+ try:
281
+ return {entry.name for entry in self._dir.iterdir() if entry.is_file() and entry.suffix == ".jsonl"}
282
+ except (FileNotFoundError, NotADirectoryError):
283
+ return set()
284
+ except OSError as exc:
285
+ logger.debug(
286
+ "[JsonlDirWatcher] snapshot failed dir=%s exc=%s",
287
+ self._dir,
288
+ exc,
289
+ )
290
+ return set()
291
+
292
+ async def _run(self) -> None:
293
+ # Wait for the directory to appear if it doesn't yet (first run
294
+ # before claude has materialised the projects dir).
295
+ while not self._dir.exists():
296
+ if self._stopped.is_set():
297
+ return
298
+ await asyncio.sleep(self._poll_interval)
299
+ # Note: if the directory appears mid-wait we re-snapshot at
300
+ # next iteration; first new file detected only AFTER the
301
+ # baseline is established, which matches the documented
302
+ # "files at start time are baseline" semantic.
303
+
304
+ while not self._stopped.is_set():
305
+ current = self._snapshot()
306
+ new = current - self._baseline
307
+ if new:
308
+ # Update baseline BEFORE firing the callback so a slow
309
+ # handler doesn't cause the same file to fire twice on
310
+ # the next tick. Order within the new set is undefined;
311
+ # callers shouldn't depend on ordering across multiple
312
+ # additions in one poll interval.
313
+ self._baseline |= new
314
+ for name in new:
315
+ path = self._dir / name
316
+ try:
317
+ await self._on_new_file(path)
318
+ except asyncio.CancelledError:
319
+ raise
320
+ except Exception as exc: # pragma: no cover
321
+ logger.warning(
322
+ "[JsonlDirWatcher] on_new_file handler raised " "dir=%s file=%s exc=%s",
323
+ self._dir.name,
324
+ name,
325
+ exc,
326
+ )
327
+
328
+ try:
329
+ await asyncio.wait_for(
330
+ self._stopped.wait(),
331
+ timeout=self._poll_interval,
332
+ )
333
+ except asyncio.TimeoutError:
334
+ pass
335
+
336
+
337
+ def snapshot_jsonl_sizes(project_dir: Path) -> Dict[str, int]:
338
+ """Return ``{name: size_bytes}`` for every ``.jsonl`` in ``project_dir``.
339
+
340
+ Used by ``ClaudeSessionPool._wait_for_session_jsonl`` (and the
341
+ equivalent in :class:`AICliSession`) to baseline the project dir
342
+ before spawning ``claude``. Post-spawn the caller polls again and
343
+ picks whichever file is new (absent from baseline) or grew
344
+ (``current_size > baseline_size``) — both fresh spawns and
345
+ ``--continue`` spawns produce a size-positive delta.
346
+
347
+ Returns an empty dict if ``project_dir`` doesn't exist yet (first
348
+ spawn under a new cwd). Silently skips entries the filesystem
349
+ refuses to ``stat`` — they're typically transient and the post-spawn
350
+ poll will catch them on the next tick once they settle.
351
+ """
352
+ sizes: Dict[str, int] = {}
353
+ try:
354
+ entries = list(project_dir.iterdir())
355
+ except (FileNotFoundError, NotADirectoryError):
356
+ return sizes
357
+ except OSError:
358
+ return sizes
359
+ for entry in entries:
360
+ if entry.suffix != ".jsonl":
361
+ continue
362
+ try:
363
+ sizes[entry.name] = entry.stat().st_size
364
+ except OSError:
365
+ continue
366
+ return sizes
367
+
368
+
369
+ def session_uuid_from_jsonl_path(path: Path) -> Optional[str]:
370
+ """Extract the session UUID from a JSONL filename, or None if the
371
+ name doesn't match the ``<uuid>.jsonl`` convention.
372
+
373
+ Cheap helper so callers don't need to manually do ``path.stem`` and
374
+ validate. Claude's filenames are RFC-4122 UUIDs; we don't validate
375
+ the UUID format aggressively since claude controls the writer.
376
+ """
377
+ if path.suffix != ".jsonl":
378
+ return None
379
+ stem = path.stem
380
+ return stem if stem else None
@@ -25,7 +25,11 @@ logger = logging.getLogger(__name__)
25
25
 
26
26
 
27
27
  def _lockfile_path(
28
- *, ide_lockfile_dir: Path, pid: int, port: int, ide_name: str,
28
+ *,
29
+ ide_lockfile_dir: Path,
30
+ pid: int,
31
+ port: int,
32
+ ide_name: str,
29
33
  ) -> Path:
30
34
  if ide_name == "gemini":
31
35
  return ide_lockfile_dir / f"gemini-ide-server-{pid}-{port}.json"
@@ -51,7 +55,10 @@ def write_ide_lockfile(
51
55
  """Write a VSCode-style IDE lockfile (mode 0600 on POSIX)."""
52
56
  ide_lockfile_dir.mkdir(parents=True, exist_ok=True)
53
57
  path = _lockfile_path(
54
- ide_lockfile_dir=ide_lockfile_dir, pid=pid, port=port, ide_name=ide_name,
58
+ ide_lockfile_dir=ide_lockfile_dir,
59
+ pid=pid,
60
+ port=port,
61
+ ide_name=ide_name,
55
62
  )
56
63
 
57
64
  # FastMCP's ``streamable_http_app()`` registers the JSON-RPC route at