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,482 @@
1
+ """MachinaOs MCP server (VSCode pattern, no custom IPC).
2
+
3
+ Hosts a `FastMCP` ASGI sub-app at ``/mcp/ide`` that spawned CLI sessions
4
+ auto-discover via the lockfile written by ``lockfile.py``. Each session
5
+ gets a per-batch bearer token; the middleware validates it and binds the
6
+ matching ``BatchContext`` into a contextvar so tool implementations can
7
+ scope to the calling session's workspace_dir / connected_skill_names /
8
+ allowed_credentials without explicit plumbing.
9
+
10
+ Tools (5 in v1, mirroring Claude Code's progressive-disclosure pattern):
11
+ - ``getWorkspaceFiles`` — glob/read inside the session's worktree
12
+ - ``listSkills`` — metadata for skills connected to the parent agent
13
+ - ``getSkill`` — full skill markdown + scripts + references
14
+ - ``getCredential`` — gated by per-batch allowlist
15
+ - ``broadcastLog`` — write to MachinaOs Terminal tab
16
+
17
+ The server module exposes:
18
+ - :func:`get_mcp_app` — Starlette/ASGI sub-app for ``app.mount(...)``
19
+ - :func:`register_batch` / :func:`unregister_batch` — `AICliService`
20
+ calls these around `run_batch()` to register/expire tokens
21
+ - :class:`BatchContext` — scoping data attached to each token
22
+
23
+ Tools deferred to v2: ``getDiagnostics``, ``executeCode``.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import contextvars
29
+ import logging
30
+ import secrets
31
+ from dataclasses import dataclass, field
32
+ from pathlib import Path
33
+ from typing import Any, Awaitable, Callable, Dict, List, Optional, Set
34
+
35
+ from starlette.middleware.base import BaseHTTPMiddleware
36
+ from starlette.requests import Request
37
+ from starlette.responses import JSONResponse, Response
38
+ from starlette.routing import Mount
39
+
40
+ from core.logging import get_logger
41
+
42
+ logger = get_logger(__name__)
43
+
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # BatchContext + token registry
47
+ # ---------------------------------------------------------------------------
48
+
49
+ @dataclass
50
+ class BatchContext:
51
+ """Scoping data attached to one batch's bearer token.
52
+
53
+ Populated at ``AICliService.run_batch()`` entry; deregistered in the
54
+ ``finally`` block. Tools dereference the calling batch via the
55
+ bearer token in `Authorization` header.
56
+ """
57
+ workflow_id: str
58
+ node_id: str
59
+ workspace_dir: Path
60
+ connected_skill_names: Set[str] = field(default_factory=set)
61
+ allowed_credentials: Set[str] = field(default_factory=set)
62
+ # Connected ``input-tools`` nodes (entries from
63
+ # ``services.plugin.edge_walker.collect_agent_connections``). Each
64
+ # dict: ``{node_id, node_type, label, parameters, ...}``. Drives the
65
+ # ``listMachinaOsTools`` / ``callMachinaOsTool`` MCP tools so the
66
+ # CLI agent sees the same tool surface the AI Agent does.
67
+ connected_tools: List[Dict[str, Any]] = field(default_factory=list)
68
+ # Optional broadcaster for `broadcastLog`. Lazily resolved from the
69
+ # global container if None.
70
+ broadcaster: Optional[Any] = None
71
+
72
+
73
+ # Token -> BatchContext registry. Lives in-memory only; tokens never
74
+ # touch disk or the credentials.db.
75
+ _active_tokens: Dict[str, BatchContext] = {}
76
+
77
+
78
+ def issue_token() -> str:
79
+ """Mint a new bearer token (32 bytes hex)."""
80
+ return secrets.token_hex(32)
81
+
82
+
83
+ def register_batch(token: str, ctx: BatchContext) -> None:
84
+ """Register a batch's auth token + expose its connected workflow
85
+ tools on the FastMCP server. Idempotent on identical context."""
86
+ if token in _active_tokens:
87
+ existing = _active_tokens[token]
88
+ if existing is not ctx:
89
+ raise ValueError("Token collision in MCP server batch registry")
90
+ return
91
+ _active_tokens[token] = ctx
92
+ tool_names = [t.get("node_type") for t in ctx.connected_tools]
93
+ logger.info(
94
+ "[CC-Agent MCP register_batch] node=%s wf=%s token=%s... "
95
+ "skills=%d tools=%d creds=%d %s",
96
+ ctx.node_id, ctx.workflow_id, token[:8],
97
+ len(ctx.connected_skill_names), len(ctx.connected_tools),
98
+ len(ctx.allowed_credentials),
99
+ f"tools={tool_names}" if tool_names else "(no tools wired)",
100
+ )
101
+ # Per-batch workflow-tool exposure lives in workflow_tools.py.
102
+ from services.cli_agent.workflow_tools import expose_workflow_tools
103
+ expose_workflow_tools(ctx.connected_tools)
104
+
105
+
106
+ def unregister_batch(token: str) -> None:
107
+ """Drop a batch's token + un-expose its tools when the refcount
108
+ hits zero. Safe to call twice."""
109
+ ctx = _active_tokens.pop(token, None)
110
+ if ctx is None:
111
+ return
112
+ logger.debug("[CC-Agent MCP] unregistered batch token=%s...", token[:8])
113
+ from services.cli_agent.workflow_tools import unexpose_workflow_tools
114
+ unexpose_workflow_tools(ctx.connected_tools)
115
+
116
+
117
+ def lookup_batch(token: str) -> Optional[BatchContext]:
118
+ return _active_tokens.get(token)
119
+
120
+
121
+ def active_batch_count() -> int:
122
+ return len(_active_tokens)
123
+
124
+
125
+ # ---------------------------------------------------------------------------
126
+ # ContextVar — thread/task-local handle to the current batch
127
+ # ---------------------------------------------------------------------------
128
+
129
+ _current_batch: contextvars.ContextVar[Optional[BatchContext]] = (
130
+ contextvars.ContextVar("machina_current_batch", default=None)
131
+ )
132
+
133
+
134
+ def _require_batch() -> BatchContext:
135
+ ctx = _current_batch.get()
136
+ if ctx is None:
137
+ raise RuntimeError(
138
+ "MCP tool called without an active batch context. "
139
+ "This indicates the auth middleware was bypassed."
140
+ )
141
+ return ctx
142
+
143
+
144
+ # Per-batch workflow-tool exposure happens in `_expose_workflow_tools`
145
+ # (above). Each connected node lands as its own
146
+ # `mcp__machinaos__<node_type>` entry; FastMCP infers the inputSchema
147
+ # from the typed `params` annotation. The legacy generic wrapper has
148
+ # been removed.
149
+
150
+
151
+ # ---------------------------------------------------------------------------
152
+ # Auth middleware
153
+ # ---------------------------------------------------------------------------
154
+
155
+ class _BearerAuthMiddleware(BaseHTTPMiddleware):
156
+ """Validate `Authorization: Bearer <token>` against the registry.
157
+
158
+ On success: bind the matching `BatchContext` into the contextvar.
159
+ On failure: 401.
160
+
161
+ The MCP spec uses MCP-Protocol-Version + Authorization headers; we
162
+ care only about the Bearer here. Health checks under `/healthz`
163
+ bypass auth (they're for dev sanity).
164
+ """
165
+
166
+ async def dispatch( # type: ignore[override]
167
+ self,
168
+ request: Request,
169
+ call_next: Callable[[Request], Awaitable[Response]],
170
+ ) -> Response:
171
+ path = request.url.path
172
+ method = request.method
173
+ if path.endswith("/healthz"):
174
+ return await call_next(request)
175
+
176
+ auth = request.headers.get("authorization") or request.headers.get(
177
+ "Authorization"
178
+ )
179
+ token: Optional[str] = None
180
+ if auth and auth.lower().startswith("bearer "):
181
+ token = auth[7:].strip() or None
182
+
183
+ if not token:
184
+ ua = request.headers.get("user-agent", "")
185
+ logger.warning(
186
+ "[CC-Agent MCP auth] %s %s -> 401 (no Bearer token; "
187
+ "auth_header=%r ua=%r) — claude CLI either didn't read the "
188
+ "lockfile or is hitting the wrong URL.",
189
+ method, path, auth, ua,
190
+ )
191
+ return JSONResponse(
192
+ {"error": "missing or malformed Authorization header"},
193
+ status_code=401,
194
+ )
195
+
196
+ ctx = lookup_batch(token)
197
+ if ctx is None:
198
+ logger.warning(
199
+ "[CC-Agent MCP auth] %s %s -> 401 (token=%s... not in "
200
+ "active registry — batch may have ended)",
201
+ method, path, token[:8],
202
+ )
203
+ return JSONResponse(
204
+ {"error": "invalid or expired token"},
205
+ status_code=401,
206
+ )
207
+
208
+ logger.info(
209
+ "[CC-Agent MCP auth] %s %s -> OK (node=%s wf=%s token=%s...)",
210
+ method, path, ctx.node_id, ctx.workflow_id, token[:8],
211
+ )
212
+
213
+ reset_token = _current_batch.set(ctx)
214
+ try:
215
+ return await call_next(request)
216
+ finally:
217
+ _current_batch.reset(reset_token)
218
+
219
+
220
+ # ---------------------------------------------------------------------------
221
+ # Tool registration helper — defers FastMCP import so module import is cheap
222
+ # ---------------------------------------------------------------------------
223
+
224
+ def _build_tools(mcp: Any) -> None: # FastMCP type
225
+ """Register the 5 v1 tools on a `FastMCP` instance."""
226
+
227
+ @mcp.tool(
228
+ name="getWorkspaceFiles",
229
+ description=(
230
+ "List or read files inside the calling session's per-task git "
231
+ "worktree. Use `read=False` for metadata-only listings; "
232
+ "`read=True` to fetch file contents (capped at 1MB per file)."
233
+ ),
234
+ )
235
+ def get_workspace_files(
236
+ path: str = ".",
237
+ pattern: str = "*",
238
+ read: bool = False,
239
+ max_bytes: int = 1_000_000,
240
+ ) -> Dict[str, Any]:
241
+ ctx = _require_batch()
242
+ try:
243
+ base = ctx.workspace_dir.resolve()
244
+ target = (base / path).resolve()
245
+ # Path-traversal guard
246
+ try:
247
+ target.relative_to(base)
248
+ except ValueError:
249
+ return {
250
+ "error": "path escapes workspace_dir",
251
+ "path": str(path),
252
+ }
253
+
254
+ if not target.exists():
255
+ return {"files": [], "path": str(path)}
256
+
257
+ entries: List[Dict[str, Any]] = []
258
+ if target.is_file():
259
+ files_iter = [target]
260
+ else:
261
+ files_iter = sorted(target.rglob(pattern))
262
+
263
+ for p in files_iter:
264
+ if not p.is_file():
265
+ continue
266
+ try:
267
+ rel = str(p.relative_to(base))
268
+ info: Dict[str, Any] = {
269
+ "path": rel,
270
+ "size": p.stat().st_size,
271
+ "mtime": p.stat().st_mtime,
272
+ }
273
+ if read and p.stat().st_size <= max_bytes:
274
+ try:
275
+ info["content"] = p.read_text(
276
+ encoding="utf-8", errors="replace"
277
+ )
278
+ except OSError as exc:
279
+ info["read_error"] = str(exc)
280
+ entries.append(info)
281
+ except OSError:
282
+ continue
283
+
284
+ if len(entries) >= 1000:
285
+ break
286
+
287
+ return {"files": entries, "path": str(path)}
288
+ except Exception as exc: # pragma: no cover — defensive
289
+ logger.exception("[CC-Agent MCP] getWorkspaceFiles failed")
290
+ return {"error": str(exc), "path": str(path)}
291
+
292
+ @mcp.tool(
293
+ name="listSkills",
294
+ description=(
295
+ "List skills connected to the parent agent node. Returns "
296
+ "metadata only (~100 tokens per skill). Call `getSkill(name)` "
297
+ "to fetch the full instructions."
298
+ ),
299
+ )
300
+ def list_skills() -> Dict[str, Any]:
301
+ ctx = _require_batch()
302
+ try:
303
+ from services.skill_loader import get_skill_loader
304
+ loader = get_skill_loader()
305
+ registry = loader.scan_skills()
306
+ results = []
307
+ for name in sorted(ctx.connected_skill_names):
308
+ meta = registry.get(name)
309
+ if meta is None:
310
+ continue
311
+ results.append({
312
+ "name": meta.name,
313
+ "description": meta.description,
314
+ "allowed_tools": list(meta.allowed_tools),
315
+ "category": (
316
+ meta.metadata.get("category")
317
+ if isinstance(meta.metadata, dict) else None
318
+ ),
319
+ })
320
+ return {"skills": results}
321
+ except Exception as exc: # pragma: no cover
322
+ logger.exception("[CC-Agent MCP] listSkills failed")
323
+ return {"error": str(exc), "skills": []}
324
+
325
+ @mcp.tool(
326
+ name="getSkill",
327
+ description=(
328
+ "Fetch full content for one skill: instructions (markdown), "
329
+ "scripts (executable code samples), and references (extra "
330
+ "docs). The skill must be connected to the parent agent node."
331
+ ),
332
+ )
333
+ def get_skill(name: str) -> Dict[str, Any]:
334
+ ctx = _require_batch()
335
+ if name not in ctx.connected_skill_names:
336
+ return {
337
+ "error": f"skill {name!r} is not connected to this agent node",
338
+ "name": name,
339
+ }
340
+ try:
341
+ from services.skill_loader import get_skill_loader
342
+ loader = get_skill_loader()
343
+ skill = loader.load_skill(name)
344
+ if skill is None:
345
+ return {"error": f"skill {name!r} not found", "name": name}
346
+ return {
347
+ "name": skill.metadata.name,
348
+ "description": skill.metadata.description,
349
+ "instructions": skill.instructions,
350
+ "allowed_tools": list(skill.metadata.allowed_tools),
351
+ "metadata": dict(skill.metadata.metadata) if skill.metadata.metadata else {},
352
+ "scripts": dict(skill.scripts),
353
+ "references": dict(skill.references),
354
+ # `assets` (binary) excluded by default — too big for MCP responses.
355
+ }
356
+ except Exception as exc: # pragma: no cover
357
+ logger.exception("[CC-Agent MCP] getSkill failed for %r", name)
358
+ return {"error": str(exc), "name": name}
359
+
360
+ @mcp.tool(
361
+ name="getCredential",
362
+ description=(
363
+ "Fetch a credential by provider name. Only credentials in the "
364
+ "batch's allowlist are returned; everything else returns 403. "
365
+ "Use sparingly — prefer the CLI's own auth where possible."
366
+ ),
367
+ )
368
+ async def get_credential(name: str) -> Dict[str, Any]:
369
+ ctx = _require_batch()
370
+ if name not in ctx.allowed_credentials:
371
+ return {
372
+ "error": f"credential {name!r} not in allowlist for this batch",
373
+ "name": name,
374
+ "status": 403,
375
+ }
376
+ try:
377
+ from core.container import container
378
+ auth = container.auth_service()
379
+ value = await auth.get_api_key(name)
380
+ if not value:
381
+ return {
382
+ "error": f"credential {name!r} not configured",
383
+ "name": name,
384
+ "status": 404,
385
+ }
386
+ return {"name": name, "value": value}
387
+ except Exception as exc: # pragma: no cover
388
+ logger.exception("[CC-Agent MCP] getCredential failed for %r", name)
389
+ return {"error": str(exc), "name": name, "status": 500}
390
+
391
+ # Per-batch workflow tools are exposed dynamically on
392
+ # `register_batch` via `_expose_workflow_tools` — each connected
393
+ # node lands as its own `mcp__machinaos__<node_type>` entry. No
394
+ # generic `listMachinaOsTools` / `callMachinaOsTool` wrapper.
395
+
396
+ @mcp.tool(
397
+ name="broadcastLog",
398
+ description=(
399
+ "Write a log line to the MachinaOs Terminal tab. Use to "
400
+ "surface intermediate progress that would otherwise be lost "
401
+ "between CLI sessions."
402
+ ),
403
+ )
404
+ async def broadcast_log(
405
+ message: str,
406
+ level: str = "info",
407
+ source: Optional[str] = None,
408
+ ) -> Dict[str, Any]:
409
+ ctx = _require_batch()
410
+ if level not in ("debug", "info", "warning", "error"):
411
+ level = "info"
412
+ try:
413
+ broadcaster = ctx.broadcaster
414
+ if broadcaster is None:
415
+ from services.status_broadcaster import get_status_broadcaster
416
+ broadcaster = get_status_broadcaster()
417
+ payload = {
418
+ "source": source or f"mcp:{ctx.node_id}",
419
+ "level": level,
420
+ "message": message[:5000],
421
+ }
422
+ await broadcaster.broadcast_terminal_log(payload)
423
+ return {"success": True}
424
+ except Exception as exc: # pragma: no cover
425
+ logger.exception("[CC-Agent MCP] broadcastLog failed")
426
+ return {"error": str(exc)}
427
+
428
+
429
+ # ---------------------------------------------------------------------------
430
+ # ASGI sub-app factory (mounted in main.py lifespan)
431
+ # ---------------------------------------------------------------------------
432
+
433
+ _app_singleton: Optional[Any] = None # Starlette app
434
+ _mcp_singleton: Optional[Any] = None # FastMCP instance — used by per-batch
435
+ # dynamic tool (un)registration.
436
+
437
+
438
+ def get_mcp_app() -> Any:
439
+ """Return the Starlette/ASGI app to mount under `/mcp/ide`.
440
+
441
+ Idempotent — multiple calls return the same instance, so the FastAPI
442
+ lifespan can wire it without worrying about duplicate registration.
443
+ """
444
+ global _app_singleton, _mcp_singleton
445
+ if _app_singleton is not None:
446
+ return _app_singleton
447
+
448
+ from mcp.server.fastmcp import FastMCP
449
+
450
+ mcp = FastMCP(
451
+ name="machinaos-cli-agent",
452
+ instructions=(
453
+ "MachinaOs IDE MCP server. Exposes workspace files, connected "
454
+ "skills, scoped credentials, and a Terminal-tab log channel "
455
+ "to the calling CLI session."
456
+ ),
457
+ # FastMCP's HTTP path defaults are fine; we mount the whole app
458
+ # under /mcp/ide externally.
459
+ )
460
+ _build_tools(mcp)
461
+ _mcp_singleton = mcp
462
+
463
+ asgi_app = mcp.streamable_http_app()
464
+ asgi_app.add_middleware(_BearerAuthMiddleware)
465
+
466
+ _app_singleton = asgi_app
467
+ return _app_singleton
468
+
469
+
470
+ # ---------------------------------------------------------------------------
471
+ # Test/diagnostic helpers (used by tests + verification step #11/#12)
472
+ # ---------------------------------------------------------------------------
473
+
474
+ def _reset_for_tests() -> None: # pragma: no cover
475
+ """Wipe the token registry + per-batch workflow-tool refcounts.
476
+ ONLY use in tests."""
477
+ global _app_singleton, _mcp_singleton
478
+ _active_tokens.clear()
479
+ _app_singleton = None
480
+ _mcp_singleton = None
481
+ from services.cli_agent.workflow_tools import _reset_for_tests as _wt_reset
482
+ _wt_reset()
@@ -0,0 +1,173 @@
1
+ """Protocol for AI CLI providers (Claude Code, Codex, Gemini).
2
+
3
+ Mirrors `services/llm/protocol.py` shape: a structurally-typed Protocol
4
+ with a small, explicit surface. Each concrete provider lives under
5
+ `providers/<vendor>.py`.
6
+
7
+ The framework spawns N parallel CLI sessions (one `AICliSession` each,
8
+ backed by `BaseProcessSupervisor`) over a list of tasks. Per-CLI
9
+ differences (argv, JSON event schema, auth handling, feature support)
10
+ are isolated to the provider; the session/pool/service layer is generic.
11
+
12
+ Auth: native CLI handles its own tokens. We only trigger the login flow
13
+ (`login_argv()`) and detect logged-in state (`auth_status_argv()` +
14
+ `detect_auth_error()`). No credential wrapping, no `~/.claude-machina/`.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass, field
20
+ from pathlib import Path
21
+ from typing import Any, Dict, List, Optional, Protocol, runtime_checkable
22
+
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Shared data types
26
+ # ---------------------------------------------------------------------------
27
+
28
+ @dataclass
29
+ class CanonicalUsage:
30
+ """Vendor-normalised token counts.
31
+
32
+ Pattern from Hermes `agent/usage_pricing.py:CanonicalUsage` — every
33
+ provider's usage shape (Anthropic vs Codex vs OpenAI handle cache
34
+ differently) maps into this so the existing `services/pricing.py`
35
+ can compute USD without per-vendor branches.
36
+ """
37
+ input_tokens: int = 0
38
+ output_tokens: int = 0
39
+ cache_read: int = 0
40
+ cache_write: int = 0
41
+ reasoning_tokens: int = 0
42
+ request_count: int = 0
43
+
44
+
45
+ @dataclass
46
+ class SessionResult:
47
+ """Per-task result returned by `AICliSession`.
48
+
49
+ Shared keys are the schema; vendor extras live in `provider_data`.
50
+ """
51
+ task_id: str
52
+ session_id: Optional[str] = None
53
+ provider: str = ""
54
+ prompt: str = ""
55
+ branch: Optional[str] = None
56
+ worktree_path: Optional[str] = None
57
+ response: str = ""
58
+ cost_usd: Optional[float] = None
59
+ duration_ms: Optional[int] = None
60
+ num_turns: Optional[int] = None
61
+ tool_calls: int = 0
62
+ canonical_usage: CanonicalUsage = field(default_factory=CanonicalUsage)
63
+ provider_data: Dict[str, Any] = field(default_factory=dict)
64
+ success: bool = False
65
+ error: Optional[str] = None
66
+
67
+
68
+ @dataclass
69
+ class BatchResult:
70
+ """Aggregated result returned by `AICliService.run_batch()`."""
71
+ tasks: List[SessionResult] = field(default_factory=list)
72
+ n_tasks: int = 0
73
+ n_succeeded: int = 0
74
+ n_failed: int = 0
75
+ total_cost_usd: Optional[float] = None
76
+ wall_clock_ms: int = 0
77
+ budget_remaining_usd: Optional[float] = None
78
+ provider: str = ""
79
+ timestamp: str = ""
80
+
81
+
82
+ # ---------------------------------------------------------------------------
83
+ # Provider protocol (structural typing)
84
+ # ---------------------------------------------------------------------------
85
+
86
+ @runtime_checkable
87
+ class AICliProvider(Protocol):
88
+ """Structural Protocol for an AI CLI provider.
89
+
90
+ Concrete classes:
91
+ - `providers.anthropic_claude.AnthropicClaudeProvider`
92
+ - `providers.openai_codex.OpenAICodexProvider`
93
+ - `providers.google_gemini.GoogleGeminiProvider` (v2 stub)
94
+ """
95
+
96
+ name: str # "claude" | "codex" | "gemini"
97
+ package_name: str # npm package
98
+ binary_name: str # "claude" | "codex" | "gemini"
99
+ ide_lock_env_var: Optional[str] # CLAUDE_IDE_LOCK | GEMINI_IDE_LOCK | None
100
+ ide_lockfile_dir: Optional[Path] # ~/.claude/ide | <tmpdir>/gemini/ide
101
+
102
+ # ---- spawn surface ---------------------------------------------------
103
+
104
+ def binary_path(self) -> Path: ...
105
+ # Resolve the CLI binary. Resolution chain (Composio pattern):
106
+ # 1) shutil.which(<binary_name>)
107
+ # 2) `npx --yes <package_name>` shim path
108
+ # Raises FileNotFoundError if neither is available.
109
+
110
+ def headless_argv(self, task: Any, *, defaults: Dict[str, Any]) -> List[str]: ...
111
+ # Build the full argv (binary + flags) for a headless run of
112
+ # `task` (a `<Provider>TaskSpec` Pydantic model). `defaults`
113
+ # comes from `ai_cli_providers.json` for this provider.
114
+
115
+ # ---- native auth (no token wrapping) --------------------------------
116
+
117
+ def login_argv(self) -> List[str]: ...
118
+ # CLI's own login command, e.g. ["claude", "login"]. Spawned
119
+ # interactively from the Credentials Modal. CLI stores its own
120
+ # credentials in `~/.claude/`, `~/.codex/`, `~/.gemini/`.
121
+
122
+ def auth_status_argv(self) -> Optional[List[str]]: ...
123
+ # No-op invocation to verify auth, e.g. ["claude", "--print", "-p", "ok"].
124
+ # Returns None if no cheap probe exists; in that case the framework
125
+ # infers from the first session's stderr.
126
+
127
+ def detect_auth_error(self, stderr: str, exit_code: int) -> bool: ...
128
+ # Match "not logged in" patterns:
129
+ # Claude: "Please run 'claude login'"
130
+ # Codex: HTTP 401 / "OPENAI_API_KEY not set"
131
+ # Gemini: exit_code == 1 (FatalAuthenticationError)
132
+
133
+ # ---- streaming output parsing ---------------------------------------
134
+
135
+ def parse_event(self, line: str) -> Optional[Dict[str, Any]]: ...
136
+ # Parse a single NDJSON line from stdout. Return None for
137
+ # un-parseable garbage.
138
+
139
+ def is_final_event(self, event: Dict[str, Any]) -> bool: ...
140
+ # True if this event marks end-of-task. For Claude: `type=="result"`.
141
+ # For Gemini: `type=="result"`. For Codex: `type=="complete"` or
142
+ # heuristic fallback.
143
+
144
+ def event_to_session_result(
145
+ self,
146
+ events: List[Dict[str, Any]],
147
+ stderr: str,
148
+ exit_code: int,
149
+ ) -> Dict[str, Any]: ...
150
+ # Reconstruct a partial dict of `SessionResult` fields from the
151
+ # event stream. Returns:
152
+ # {
153
+ # "session_id": ..., "response": ..., "cost_usd": ...,
154
+ # "duration_ms": ..., "num_turns": ..., "tool_calls": ...,
155
+ # "success": bool, "error": Optional[str],
156
+ # "canonical_usage": CanonicalUsage,
157
+ # "provider_data": {<vendor-specific>},
158
+ # }
159
+ # provider_data carries vendor-only metadata (Anthropic
160
+ # reasoning_details, Codex call_id, Gemini extra_content) without
161
+ # bloating the shared schema. Pattern from
162
+ # Hermes agent/transports/types.py NormalizedResponse.
163
+
164
+ def canonical_usage(self, events: List[Dict[str, Any]]) -> CanonicalUsage: ...
165
+ # Normalise vendor token-counting into the shared `CanonicalUsage`
166
+ # shape. Pattern from Hermes agent/usage_pricing.py.
167
+
168
+ # ---- feature gating --------------------------------------------------
169
+
170
+ def supports(self, feature: str) -> bool: ...
171
+ # Feature flags consulted by the session/service layer.
172
+ # Recognised: "max_budget", "max_turns", "session_id", "resume",
173
+ # "mcp_runtime", "json_cost", "ide_lockfile", "sandbox".
@@ -0,0 +1,9 @@
1
+ """Concrete `AICliProvider` implementations.
2
+
3
+ - `anthropic_claude.AnthropicClaudeProvider` — full v1 surface
4
+ - `openai_codex.OpenAICodexProvider` — sandbox-first, no session
5
+ - `google_gemini.GoogleGeminiProvider` — v2 stub raising NotImplementedError
6
+
7
+ Imports are intentionally not eager — `factory.create_cli_provider()`
8
+ lazy-imports each one.
9
+ """