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.
- package/.env.template +74 -5
- package/{workflows/AI Assistant_workflow-1778504793388-ou1m1tz2x.json → .machina/workflows/AI Assistant_example_workflow-1779017037684-e2e5da7a.json } +164 -105
- package/{workflows/AI Employee_example_workflow-1777720598005-u4cm858dv.json → .machina/workflows/AI Employee_example_workflow-1779102911870-cbc76c82.json } +582 -328
- package/{workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json → .machina/workflows/Claude Assistant_example_workflow-1779095939967-2369cff4.json } +152 -83
- package/README.md +5 -2
- package/bin/cli.js +2 -2
- package/{machina → cli}/__main__.py +11 -7
- package/cli/_common.py +122 -0
- package/cli/buildenv.py +40 -0
- package/cli/cli.py +204 -0
- package/{machina → cli}/colors.py +10 -2
- package/cli/commands/__init__.py +1 -0
- package/cli/commands/_temporal_specs.py +59 -0
- package/{machina → cli}/commands/build.py +35 -45
- package/cli/commands/clean.py +141 -0
- package/cli/commands/daemon/__init__.py +47 -0
- package/cli/commands/daemon/_state.py +97 -0
- package/cli/commands/daemon/restart.py +14 -0
- package/cli/commands/daemon/start.py +49 -0
- package/cli/commands/daemon/status.py +20 -0
- package/cli/commands/daemon/stop.py +22 -0
- package/{machina → cli}/commands/dev.py +32 -42
- package/{machina → cli}/commands/docs.py +13 -11
- package/{machina → cli}/commands/start.py +69 -62
- package/{machina → cli}/commands/stop.py +7 -10
- package/{machina → cli}/commands/version.py +12 -6
- package/cli/config.py +170 -0
- package/cli/platform_.py +169 -0
- package/{machina → cli}/ports.py +42 -3
- package/{machina → cli}/run.py +29 -2
- package/{machina → cli}/supervisor.py +29 -12
- package/{machina → cli}/tcp.py +6 -2
- package/{machina → cli}/tree.py +38 -11
- package/client/dist/assets/{ActionBar-Du2MSFSz.js → ActionBar-Cjr3TF7g.js} +1 -1
- package/client/dist/assets/{ApiKeyInput-k2LBmBjb.js → ApiKeyInput-DIJE2PVA.js} +1 -1
- package/client/dist/assets/{ApiKeyPanel-C_bV9U0X.js → ApiKeyPanel-CPmye7uh.js} +1 -1
- package/client/dist/assets/{ApiUsageSection-CmVfwZzL.js → ApiUsageSection-TF_7gH2D.js} +1 -1
- package/client/dist/assets/{EmailPanel-CeKIMGu-.js → EmailPanel-Bs-xvbKR.js} +1 -1
- package/client/dist/assets/{OAuthPanel-KA3t3Q2K.js → OAuthPanel-BDtVJhAV.js} +1 -1
- package/client/dist/assets/{QrPairingPanel-NgNpJNuk.js → QrPairingPanel-BwJehTuZ.js} +1 -1
- package/client/dist/assets/{RateLimitSection-Du5YNVIA.js → RateLimitSection-CfNOoPIS.js} +1 -1
- package/client/dist/assets/{StatusCard-DNLyayXc.js → StatusCard-DkwIrgdP.js} +1 -1
- package/client/dist/assets/index-P2FzntoL.js +165 -0
- package/client/dist/index.html +1 -1
- package/client/package.json +1 -1
- package/client/src/Dashboard.tsx +128 -76
- package/client/src/adapters/nodeSpecToDescription.ts +7 -0
- package/client/src/assets/icons/index.test.ts +10 -0
- package/client/src/assets/icons/index.ts +16 -3
- package/client/src/components/AIAgentNode.tsx +8 -8
- package/client/src/components/ParameterRenderer.tsx +6 -3
- package/client/src/components/SkillEditorModal.tsx +1 -0
- package/client/src/components/credentials/panels/EmailPanel.tsx +2 -0
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +2 -0
- package/client/src/components/credentials/sections/RateLimitSection.tsx +1 -0
- package/client/src/components/icons/AIProviderIcons.tsx +1 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +1 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1 -0
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +1 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +1 -0
- package/client/src/components/ui/ComponentPalette.tsx +1 -0
- package/client/src/components/ui/MapSelector.tsx +1 -0
- package/client/src/components/ui/NodeContextMenu.tsx +3 -3
- package/client/src/components/ui/SettingsPanel.tsx +1 -0
- package/client/src/components/ui/action-button.tsx +1 -0
- package/client/src/components/ui/badge.tsx +1 -0
- package/client/src/components/ui/button.tsx +1 -0
- package/client/src/components/ui/form.tsx +1 -0
- package/client/src/components/ui/tabs.tsx +1 -0
- package/client/src/contexts/AuthContext.tsx +1 -0
- package/client/src/contexts/ThemeContext.tsx +1 -0
- package/client/src/contexts/WebSocketContext.tsx +104 -34
- package/client/src/hooks/__tests__/useApiKeys.test.ts +2 -2
- package/client/src/hooks/useReactFlowNodes.ts +1 -0
- package/client/src/hooks/useWorkflowValidation.ts +142 -0
- package/client/src/lib/nodeSpec.ts +1 -0
- package/client/src/test/providers.tsx +1 -0
- package/client/src/types/__tests__/cloudEvents.test.ts +5 -2
- package/client/src/types/cloudEvents.ts +19 -7
- package/client/src/utils/nodeUtils.ts +1 -1
- package/client/src/utils/workflow.ts +8 -2
- package/client/src/utils/workflowExport.ts +60 -3
- package/package.json +24 -23
- package/scripts/install.js +16 -27
- package/scripts/migrate_icons.py +3 -1
- package/scripts/migrate_skill_icons.py +6 -7
- package/scripts/postinstall.js +11 -9
- package/server/config/ai_cli_providers.json +2 -3
- package/server/config/credential_providers.json +15 -15
- package/server/config/llm_defaults.json +1 -1
- package/server/config/model_registry.json +416 -611
- package/server/constants.py +285 -223
- package/server/core/__init__.py +1 -1
- package/server/core/cache.py +9 -29
- package/server/core/cleanup.py +12 -24
- package/server/core/config.py +148 -24
- package/server/core/container.py +68 -59
- package/server/core/credential_backends.py +5 -13
- package/server/core/credentials_database.py +13 -43
- package/server/core/database.py +292 -353
- package/server/core/health.py +4 -5
- package/server/core/logging.py +241 -87
- package/server/core/paths.py +285 -0
- package/server/core/tracing.py +2 -8
- package/server/gunicorn.conf.py +1 -0
- package/server/main.py +150 -74
- package/server/middleware/auth.py +18 -24
- package/server/models/__init__.py +1 -1
- package/server/models/auth.py +5 -12
- package/server/models/database.py +36 -68
- package/server/models/node_metadata.py +25 -18
- package/server/nodejs/dist/index.js +107 -0
- package/server/nodes/README.md +11 -5
- package/server/nodes/__init__.py +1 -1
- package/server/nodes/_visuals.py +146 -14
- package/server/nodes/agent/_events.py +124 -0
- package/server/nodes/agent/_handles.py +15 -29
- package/server/nodes/agent/_inline.py +28 -25
- package/server/nodes/agent/_specialized.py +30 -15
- package/server/nodes/agent/{ai_agent.py → ai_agent/__init__.py} +33 -17
- package/server/nodes/agent/ai_agent/meta.json +3 -0
- package/server/nodes/agent/{ai_employee.py → ai_employee/__init__.py} +5 -2
- package/server/nodes/agent/ai_employee/meta.json +3 -0
- package/server/nodes/agent/{android_agent.py → android_agent/__init__.py} +1 -1
- package/server/nodes/agent/android_agent/meta.json +3 -0
- package/server/nodes/agent/{autonomous_agent.py → autonomous_agent/__init__.py} +2 -1
- package/server/nodes/agent/autonomous_agent/meta.json +3 -0
- package/server/nodes/agent/{chat_agent.py → chat_agent/__init__.py} +29 -12
- package/server/nodes/agent/chat_agent/meta.json +3 -0
- package/server/nodes/agent/{claude_code_agent.py → claude_code_agent/__init__.py} +192 -95
- package/server/nodes/agent/claude_code_agent/_handlers.py +169 -0
- package/server/{services/claude_oauth.py → nodes/agent/claude_code_agent/_oauth.py} +26 -13
- package/server/nodes/agent/claude_code_agent/_pool.py +1020 -0
- package/server/nodes/agent/claude_code_agent/_provider.py +513 -0
- package/server/nodes/agent/claude_code_agent/_skills.py +245 -0
- package/server/nodes/agent/claude_code_agent/meta.json +3 -0
- package/server/nodes/agent/{codex_agent.py → codex_agent/__init__.py} +26 -35
- package/server/nodes/agent/codex_agent/meta.json +3 -0
- package/server/nodes/agent/{coding_agent.py → coding_agent/__init__.py} +1 -1
- package/server/nodes/agent/coding_agent/meta.json +3 -0
- package/server/nodes/agent/{consumer_agent.py → consumer_agent/__init__.py} +1 -1
- package/server/nodes/agent/consumer_agent/meta.json +3 -0
- package/server/nodes/agent/{orchestrator_agent.py → orchestrator_agent/__init__.py} +5 -2
- package/server/nodes/agent/orchestrator_agent/meta.json +3 -0
- package/server/nodes/agent/{payments_agent.py → payments_agent/__init__.py} +1 -1
- package/server/nodes/agent/payments_agent/meta.json +3 -0
- package/server/nodes/agent/{productivity_agent.py → productivity_agent/__init__.py} +1 -1
- package/server/nodes/agent/productivity_agent/meta.json +3 -0
- package/server/nodes/agent/{rlm_agent.py → rlm_agent/__init__.py} +18 -17
- package/server/nodes/agent/rlm_agent/meta.json +3 -0
- package/server/nodes/agent/{social_agent.py → social_agent/__init__.py} +1 -1
- package/server/nodes/agent/social_agent/meta.json +3 -0
- package/server/nodes/agent/{task_agent.py → task_agent/__init__.py} +1 -1
- package/server/nodes/agent/task_agent/meta.json +3 -0
- package/server/nodes/agent/{tool_agent.py → tool_agent/__init__.py} +1 -1
- package/server/nodes/agent/tool_agent/meta.json +3 -0
- package/server/nodes/agent/{travel_agent.py → travel_agent/__init__.py} +1 -1
- package/server/nodes/agent/travel_agent/meta.json +3 -0
- package/server/nodes/agent/{web_agent.py → web_agent/__init__.py} +1 -1
- package/server/nodes/agent/web_agent/meta.json +3 -0
- package/server/nodes/android/__init__.py +24 -0
- package/server/nodes/android/_base.py +93 -76
- package/server/nodes/android/_dispatcher.py +140 -223
- package/server/nodes/android/_events.py +154 -0
- package/server/nodes/android/_handlers.py +13 -7
- package/server/nodes/android/_option_loaders.py +1 -4
- package/server/nodes/android/_refresh.py +27 -37
- package/server/nodes/android/_relay/broadcaster.py +25 -41
- package/server/nodes/android/_relay/client.py +23 -42
- package/server/nodes/android/_relay/manager.py +1 -0
- package/server/nodes/android/_relay/protocol.py +6 -0
- package/server/nodes/android/_router.py +48 -133
- package/server/nodes/android/{airplane_mode_control.py → airplane_mode_control/__init__.py} +2 -1
- package/server/nodes/android/airplane_mode_control/meta.json +3 -0
- package/server/nodes/android/{app_launcher.py → app_launcher/__init__.py} +2 -1
- package/server/nodes/android/app_launcher/meta.json +3 -0
- package/server/nodes/android/{app_list.py → app_list/__init__.py} +2 -1
- package/server/nodes/android/app_list/meta.json +3 -0
- package/server/nodes/android/{audio_automation.py → audio_automation/__init__.py} +2 -1
- package/server/nodes/android/audio_automation/meta.json +3 -0
- package/server/nodes/android/{battery_monitor.py → battery_monitor/__init__.py} +2 -1
- package/server/nodes/android/battery_monitor/meta.json +3 -0
- package/server/nodes/android/{bluetooth_automation.py → bluetooth_automation/__init__.py} +2 -1
- package/server/nodes/android/bluetooth_automation/meta.json +3 -0
- package/server/nodes/android/{camera_control.py → camera_control/__init__.py} +2 -1
- package/server/nodes/android/camera_control/meta.json +3 -0
- package/server/nodes/android/{device_state_automation.py → device_state_automation/__init__.py} +2 -1
- package/server/nodes/android/device_state_automation/meta.json +3 -0
- package/server/nodes/android/{environmental_sensors.py → environmental_sensors/__init__.py} +2 -1
- package/server/nodes/android/environmental_sensors/meta.json +3 -0
- package/server/nodes/android/{location.py → location/__init__.py} +2 -1
- package/server/nodes/android/location/meta.json +3 -0
- package/server/nodes/android/{media_control.py → media_control/__init__.py} +2 -1
- package/server/nodes/android/media_control/meta.json +3 -0
- package/server/nodes/android/{motion_detection.py → motion_detection/__init__.py} +2 -1
- package/server/nodes/android/motion_detection/meta.json +3 -0
- package/server/nodes/android/{network_monitor.py → network_monitor/__init__.py} +2 -1
- package/server/nodes/android/network_monitor/meta.json +3 -0
- package/server/nodes/android/{screen_control_automation.py → screen_control_automation/__init__.py} +2 -1
- package/server/nodes/android/screen_control_automation/meta.json +3 -0
- package/server/nodes/android/{system_info.py → system_info/__init__.py} +2 -1
- package/server/nodes/android/system_info/meta.json +3 -0
- package/server/nodes/android/{wifi_automation.py → wifi_automation/__init__.py} +2 -1
- package/server/nodes/android/wifi_automation/meta.json +3 -0
- package/server/nodes/browser/__init__.py +22 -1
- package/server/nodes/browser/_install.py +63 -0
- package/server/nodes/browser/_service.py +21 -25
- package/server/nodes/browser/{browser.py → browser/__init__.py} +58 -25
- package/server/nodes/browser/browser/meta.json +3 -0
- package/server/nodes/chat/{chat_history.py → chat_history/__init__.py} +2 -4
- package/server/nodes/chat/chat_history/meta.json +3 -0
- package/server/nodes/chat/{chat_send.py → chat_send/__init__.py} +2 -4
- package/server/nodes/chat/chat_send/icon.svg +1 -0
- package/server/nodes/chat/chat_send/meta.json +3 -0
- package/server/nodes/code/_base.py +1 -1
- package/server/nodes/code/{javascript_executor.py → javascript_executor/__init__.py} +5 -5
- package/server/nodes/code/javascript_executor/meta.json +3 -0
- package/server/nodes/code/{python_executor.py → python_executor/__init__.py} +32 -14
- package/server/nodes/code/python_executor/meta.json +3 -0
- package/server/nodes/code/{typescript_executor.py → typescript_executor/__init__.py} +5 -5
- package/server/nodes/code/typescript_executor/meta.json +3 -0
- package/server/nodes/document/{document_parser.py → document_parser/__init__.py} +26 -15
- package/server/nodes/document/document_parser/meta.json +3 -0
- package/server/nodes/document/{embedding_generator.py → embedding_generator/__init__.py} +16 -9
- package/server/nodes/document/embedding_generator/meta.json +3 -0
- package/server/nodes/document/{file_downloader.py → file_downloader/__init__.py} +30 -20
- package/server/nodes/document/file_downloader/meta.json +3 -0
- package/server/nodes/document/{http_scraper.py → http_scraper/__init__.py} +31 -21
- package/server/nodes/document/http_scraper/meta.json +3 -0
- package/server/nodes/document/{text_chunker.py → text_chunker/__init__.py} +17 -12
- package/server/nodes/document/text_chunker/meta.json +3 -0
- package/server/nodes/document/{vector_store.py → vector_store/__init__.py} +88 -72
- package/server/nodes/document/vector_store/meta.json +3 -0
- package/server/nodes/email/__init__.py +9 -2
- package/server/nodes/email/_events.py +54 -0
- package/server/nodes/email/_filters.py +3 -3
- package/server/nodes/email/_himalaya.py +95 -50
- package/server/nodes/email/_service.py +23 -13
- package/server/nodes/email/{email_read.py → email_read/__init__.py} +23 -11
- package/server/nodes/email/email_read/icon.svg +6 -0
- package/server/nodes/email/email_read/meta.json +3 -0
- package/server/nodes/email/{email_receive.py → email_receive/__init__.py} +45 -23
- package/server/nodes/email/email_receive/meta.json +3 -0
- package/server/nodes/email/{email_send.py → email_send/__init__.py} +13 -7
- package/server/nodes/email/email_send/meta.json +3 -0
- package/server/nodes/filesystem/_backend.py +1 -5
- package/server/nodes/filesystem/{file_modify.py → file_modify/__init__.py} +10 -5
- package/server/nodes/filesystem/file_modify/meta.json +3 -0
- package/server/nodes/filesystem/{file_read.py → file_read/__init__.py} +7 -3
- package/server/nodes/filesystem/file_read/meta.json +3 -0
- package/server/nodes/filesystem/{fs_search.py → fs_search/__init__.py} +11 -3
- package/server/nodes/filesystem/fs_search/meta.json +3 -0
- package/server/nodes/filesystem/{shell.py → shell/__init__.py} +12 -5
- package/server/nodes/filesystem/shell/meta.json +3 -0
- package/server/nodes/google/__init__.py +12 -0
- package/server/nodes/google/_auth_helper.py +7 -13
- package/server/nodes/google/_base.py +14 -11
- package/server/nodes/google/_credentials.py +2 -1
- package/server/nodes/google/_events.py +47 -0
- package/server/nodes/google/_filters.py +3 -3
- package/server/nodes/google/_gmail.py +70 -47
- package/server/nodes/google/_handlers.py +3 -1
- package/server/nodes/google/_oauth.py +25 -11
- package/server/nodes/google/_option_loaders.py +9 -30
- package/server/nodes/google/_refresh.py +8 -12
- package/server/nodes/google/_router.py +4 -5
- package/server/nodes/google/{calendar.py → calendar/__init__.py} +87 -64
- package/server/nodes/google/calendar/meta.json +3 -0
- package/server/nodes/google/{contacts.py → contacts/__init__.py} +84 -72
- package/server/nodes/google/contacts/meta.json +3 -0
- package/server/nodes/google/{drive.py → drive/__init__.py} +87 -72
- package/server/nodes/google/drive/meta.json +3 -0
- package/server/nodes/google/{gmail.py → gmail/__init__.py} +73 -39
- package/server/nodes/google/gmail/meta.json +3 -0
- package/server/nodes/google/{gmail_receive.py → gmail_receive/__init__.py} +31 -24
- package/server/nodes/google/gmail_receive/icon.svg +7 -0
- package/server/nodes/google/gmail_receive/meta.json +3 -0
- package/server/nodes/google/google.svg +7 -0
- package/server/nodes/google/{sheets.py → sheets/__init__.py} +54 -42
- package/server/nodes/google/sheets/meta.json +3 -0
- package/server/nodes/google/{tasks.py → tasks/__init__.py} +56 -43
- package/server/nodes/google/tasks/meta.json +3 -0
- package/server/nodes/groups.py +28 -28
- package/server/nodes/location/__init__.py +31 -1
- package/server/nodes/location/_credentials.py +1 -6
- package/server/nodes/location/_service.py +88 -107
- package/server/nodes/location/{gmaps_create.py → gmaps_create/__init__.py} +6 -6
- package/server/nodes/location/gmaps_create/meta.json +3 -0
- package/server/nodes/location/{gmaps_locations.py → gmaps_locations/__init__.py} +8 -6
- package/server/nodes/location/gmaps_locations/meta.json +3 -0
- package/server/nodes/location/{gmaps_nearby_places.py → gmaps_nearby_places/__init__.py} +8 -6
- package/server/nodes/location/gmaps_nearby_places/meta.json +3 -0
- package/server/nodes/model/_base.py +10 -7
- package/server/nodes/model/_credentials.py +10 -10
- package/server/nodes/model/_local_validator.py +28 -24
- package/server/nodes/model/{anthropic_chat_model.py → anthropic_chat_model/__init__.py} +5 -3
- package/server/nodes/model/anthropic_chat_model/meta.json +3 -0
- package/server/nodes/model/{cerebras_chat_model.py → cerebras_chat_model/__init__.py} +5 -3
- package/server/nodes/model/cerebras_chat_model/meta.json +3 -0
- package/server/nodes/model/{deepseek_chat_model.py → deepseek_chat_model/__init__.py} +8 -4
- package/server/nodes/model/deepseek_chat_model/meta.json +3 -0
- package/server/nodes/model/{gemini_chat_model.py → gemini_chat_model/__init__.py} +5 -3
- package/server/nodes/model/gemini_chat_model/meta.json +3 -0
- package/server/nodes/model/{groq_chat_model.py → groq_chat_model/__init__.py} +2 -2
- package/server/nodes/model/groq_chat_model/meta.json +3 -0
- package/server/nodes/model/{kimi_chat_model.py → kimi_chat_model/__init__.py} +2 -2
- package/server/nodes/model/kimi_chat_model/meta.json +3 -0
- package/server/nodes/model/{lmstudio_chat_model.py → lmstudio_chat_model/__init__.py} +2 -2
- package/server/nodes/model/lmstudio_chat_model/meta.json +3 -0
- package/server/nodes/model/{mistral_chat_model.py → mistral_chat_model/__init__.py} +2 -2
- package/server/nodes/model/mistral_chat_model/meta.json +3 -0
- package/server/nodes/model/{ollama_chat_model.py → ollama_chat_model/__init__.py} +2 -2
- package/server/nodes/model/ollama_chat_model/meta.json +3 -0
- package/server/nodes/model/{openai_chat_model.py → openai_chat_model/__init__.py} +8 -4
- package/server/nodes/model/openai_chat_model/meta.json +3 -0
- package/server/nodes/model/{openrouter_chat_model.py → openrouter_chat_model/__init__.py} +8 -4
- package/server/nodes/model/openrouter_chat_model/meta.json +3 -0
- package/server/nodes/proxy/_usage.py +14 -15
- package/server/nodes/proxy/{proxy_config.py → proxy_config/__init__.py} +39 -30
- package/server/nodes/proxy/proxy_config/meta.json +3 -0
- package/server/nodes/proxy/{proxy_request.py → proxy_request/__init__.py} +30 -16
- package/server/nodes/proxy/proxy_request/meta.json +3 -0
- package/server/nodes/proxy/{proxy_status.py → proxy_status/__init__.py} +2 -0
- package/server/nodes/proxy/proxy_status/meta.json +3 -0
- package/server/nodes/scheduler/{cron_scheduler.py → cron_scheduler/__init__.py} +96 -23
- package/server/nodes/scheduler/cron_scheduler/_workflow.py +155 -0
- package/server/nodes/scheduler/cron_scheduler/meta.json +3 -0
- package/server/nodes/scheduler/{timer.py → timer/__init__.py} +6 -5
- package/server/nodes/scheduler/timer/meta.json +3 -0
- package/server/nodes/scraper/_credentials.py +0 -1
- package/server/nodes/scraper/{apify_actor.py → apify_actor/__init__.py} +44 -35
- package/server/nodes/scraper/apify_actor/icon.svg +5 -0
- package/server/nodes/scraper/apify_actor/meta.json +3 -0
- package/server/nodes/scraper/{crawlee_scraper.py → crawlee_scraper/__init__.py} +96 -57
- package/server/nodes/scraper/crawlee_scraper/meta.json +3 -0
- package/server/nodes/search/{brave_search.py → brave_search/__init__.py} +6 -5
- package/server/nodes/search/brave_search/icon.svg +3 -0
- package/server/nodes/search/brave_search/meta.json +3 -0
- package/server/nodes/search/{duckduckgo_search.py → duckduckgo_search/__init__.py} +17 -6
- package/server/nodes/search/duckduckgo_search/meta.json +3 -0
- package/server/nodes/search/{perplexity_search.py → perplexity_search/__init__.py} +4 -5
- package/server/nodes/search/perplexity_search/icon.svg +3 -0
- package/server/nodes/search/perplexity_search/meta.json +3 -0
- package/server/nodes/search/{serper_search.py → serper_search/__init__.py} +32 -25
- package/server/nodes/search/serper_search/icon.svg +3 -0
- package/server/nodes/search/serper_search/meta.json +3 -0
- package/server/nodes/skill/__init__.py +21 -1
- package/server/nodes/skill/_expander.py +75 -0
- package/server/nodes/skill/{master_skill.py → master_skill/__init__.py} +2 -8
- package/server/nodes/skill/master_skill/_events.py +84 -0
- package/server/nodes/skill/master_skill/meta.json +3 -0
- package/server/nodes/skill/{simple_memory.py → simple_memory/__init__.py} +8 -16
- package/server/nodes/skill/simple_memory/meta.json +3 -0
- package/server/nodes/social/_base.py +223 -231
- package/server/nodes/social/{social_receive.py → social_receive/__init__.py} +38 -13
- package/server/nodes/social/social_receive/meta.json +3 -0
- package/server/nodes/social/{social_send.py → social_send/__init__.py} +71 -29
- package/server/nodes/social/social_send/icon.svg +1 -0
- package/server/nodes/social/social_send/meta.json +3 -0
- package/server/nodes/stripe/__init__.py +7 -3
- package/server/nodes/stripe/_credentials.py +0 -1
- package/server/nodes/stripe/_handlers.py +18 -7
- package/server/nodes/stripe/_install.py +14 -15
- package/server/nodes/stripe/_source.py +5 -5
- package/server/nodes/stripe/icon.svg +1 -0
- package/server/nodes/stripe/meta.json +3 -0
- package/server/nodes/stripe/stripe_action.py +4 -4
- package/server/nodes/stripe/stripe_receive.py +6 -9
- package/server/nodes/telegram/__init__.py +13 -0
- package/server/nodes/telegram/_credentials.py +2 -7
- package/server/nodes/telegram/_events.py +167 -0
- package/server/nodes/telegram/_filters.py +3 -11
- package/server/nodes/telegram/_handlers.py +17 -7
- package/server/nodes/telegram/_refresh.py +24 -34
- package/server/nodes/telegram/_service.py +29 -45
- package/server/nodes/telegram/meta.json +3 -0
- package/server/nodes/telegram/telegram.svg +3 -0
- package/server/nodes/telegram/telegram_receive.py +38 -18
- package/server/nodes/telegram/telegram_send.py +21 -19
- package/server/nodes/text/{file_handler.py → file_handler/__init__.py} +7 -1
- package/server/nodes/text/file_handler/meta.json +3 -0
- package/server/nodes/text/{text_generator.py → text_generator/__init__.py} +2 -1
- package/server/nodes/text/text_generator/meta.json +3 -0
- package/server/nodes/tool/{agent_builder.py → agent_builder/__init__.py} +105 -100
- package/server/nodes/tool/agent_builder/_events.py +91 -0
- package/server/nodes/tool/agent_builder/meta.json +3 -0
- package/server/nodes/tool/{calculator_tool.py → calculator_tool/__init__.py} +19 -7
- package/server/nodes/tool/calculator_tool/meta.json +3 -0
- package/server/nodes/tool/{current_time_tool.py → current_time_tool/__init__.py} +6 -4
- package/server/nodes/tool/current_time_tool/meta.json +3 -0
- package/server/nodes/tool/{task_manager.py → task_manager/__init__.py} +17 -18
- package/server/nodes/tool/task_manager/meta.json +3 -0
- package/server/nodes/tool/{write_todos.py → write_todos/__init__.py} +20 -6
- package/server/nodes/tool/write_todos/meta.json +3 -0
- package/server/nodes/trigger/{chat_trigger.py → chat_trigger/__init__.py} +11 -7
- package/server/nodes/trigger/chat_trigger/_events.py +53 -0
- package/server/nodes/trigger/chat_trigger/meta.json +3 -0
- package/server/nodes/trigger/{task_trigger.py → task_trigger/__init__.py} +10 -7
- package/server/nodes/trigger/task_trigger/meta.json +3 -0
- package/server/nodes/trigger/{webhook_trigger.py → webhook_trigger/__init__.py} +10 -7
- package/server/nodes/trigger/webhook_trigger/_events.py +54 -0
- package/server/nodes/trigger/webhook_trigger/meta.json +3 -0
- package/server/nodes/twitter/__init__.py +7 -1
- package/server/nodes/twitter/_base.py +86 -61
- package/server/nodes/twitter/_credentials.py +7 -5
- package/server/nodes/twitter/_events.py +101 -0
- package/server/nodes/twitter/_filters.py +9 -9
- package/server/nodes/twitter/_handlers.py +3 -1
- package/server/nodes/twitter/_oauth.py +1 -2
- package/server/nodes/twitter/_refresh.py +8 -12
- package/server/nodes/twitter/{twitter_receive.py → twitter_receive/__init__.py} +7 -7
- package/server/nodes/twitter/twitter_receive/icon.svg +1 -0
- package/server/nodes/twitter/twitter_receive/meta.json +3 -0
- package/server/nodes/twitter/{twitter_search.py → twitter_search/__init__.py} +16 -11
- package/server/nodes/twitter/twitter_search/icon.svg +1 -0
- package/server/nodes/twitter/twitter_search/meta.json +3 -0
- package/server/nodes/twitter/{twitter_send.py → twitter_send/__init__.py} +60 -27
- package/server/nodes/twitter/twitter_send/icon.svg +1 -0
- package/server/nodes/twitter/twitter_send/meta.json +3 -0
- package/server/nodes/twitter/{twitter_user.py → twitter_user/__init__.py} +34 -19
- package/server/nodes/twitter/twitter_user/icon.svg +1 -0
- package/server/nodes/twitter/twitter_user/meta.json +3 -0
- package/server/nodes/utility/{console.py → console/__init__.py} +17 -22
- package/server/nodes/utility/console/meta.json +3 -0
- package/server/nodes/utility/{http_request.py → http_request/__init__.py} +9 -6
- package/server/nodes/utility/http_request/meta.json +3 -0
- package/server/nodes/utility/{process_manager.py → process_manager/__init__.py} +10 -6
- package/server/nodes/utility/process_manager/meta.json +3 -0
- package/server/nodes/utility/team_monitor/meta.json +3 -0
- package/server/nodes/utility/{webhook_response.py → webhook_response/__init__.py} +12 -7
- package/server/nodes/utility/webhook_response/meta.json +3 -0
- package/server/nodes/visuals.json +69 -251
- package/server/nodes/whatsapp/__init__.py +24 -0
- package/server/nodes/whatsapp/_base.py +283 -338
- package/server/nodes/whatsapp/_credentials.py +44 -0
- package/server/nodes/whatsapp/_events.py +277 -0
- package/server/nodes/whatsapp/_filters.py +36 -37
- package/server/nodes/whatsapp/_handlers.py +2 -0
- package/server/nodes/whatsapp/_option_loaders.py +1 -3
- package/server/nodes/whatsapp/_refresh.py +13 -18
- package/server/nodes/whatsapp/_runtime.py +9 -6
- package/server/nodes/whatsapp/_service.py +89 -152
- package/server/nodes/whatsapp/meta.json +3 -0
- package/server/nodes/whatsapp/whatsapp_db.py +116 -54
- package/server/nodes/whatsapp/whatsapp_receive.py +30 -13
- package/server/nodes/whatsapp/whatsapp_send.py +60 -37
- package/server/nodes/workflow/{start.py → start/__init__.py} +1 -4
- package/server/nodes/workflow/start/meta.json +3 -0
- package/server/package-lock.json +3 -3
- package/server/package.json +3 -0
- package/server/pyproject.toml +39 -10
- package/server/requirements.txt +3 -5
- package/server/routers/__init__.py +1 -1
- package/server/routers/auth.py +16 -56
- package/server/routers/database.py +27 -50
- package/server/routers/nodejs_compat.py +25 -87
- package/server/routers/schemas.py +66 -2
- package/server/routers/webhook.py +12 -12
- package/server/routers/websocket.py +312 -1716
- package/server/routers/workflow.py +28 -53
- package/server/scripts/smoke_test_skills.py +178 -0
- package/server/services/__init__.py +1 -1
- package/server/services/_supervisor/process.py +9 -3
- package/server/services/_supervisor/registry.py +3 -3
- package/server/services/_supervisor/util.py +1 -1
- package/server/services/agent_team.py +15 -43
- package/server/services/agent_teams/__init__.py +17 -0
- package/server/services/agent_teams/handlers.py +195 -0
- package/server/services/ai.py +853 -1108
- package/server/services/auth.py +10 -34
- package/server/services/chat_client.py +5 -34
- package/server/services/circuit_breaker.py +2 -6
- package/server/services/cli_agent/__init__.py +28 -4
- package/server/services/cli_agent/_cli_auth.py +61 -0
- package/server/services/cli_agent/_handlers.py +24 -183
- package/server/services/cli_agent/config.py +5 -8
- package/server/services/cli_agent/factory.py +168 -22
- package/server/services/cli_agent/jsonl_watcher.py +380 -0
- package/server/services/cli_agent/lockfile.py +9 -2
- package/server/services/cli_agent/mcp_server.py +110 -34
- package/server/services/cli_agent/protocol.py +37 -19
- package/server/services/cli_agent/providers/__init__.py +8 -4
- package/server/services/cli_agent/providers/google_gemini.py +11 -5
- package/server/services/cli_agent/providers/openai_codex.py +34 -34
- package/server/services/cli_agent/service.py +245 -83
- package/server/services/cli_agent/session.py +409 -229
- package/server/services/cli_agent/transports/__init__.py +47 -0
- package/server/services/cli_agent/transports/base.py +111 -0
- package/server/services/cli_agent/transports/posix.py +196 -0
- package/server/services/cli_agent/transports/windows.py +189 -0
- package/server/services/cli_agent/types.py +45 -18
- package/server/services/cli_agent/workflow_tools.py +28 -15
- package/server/services/compaction.py +68 -52
- package/server/services/credential_registry.py +6 -20
- package/server/services/credentials/__init__.py +18 -0
- package/server/services/credentials/handlers.py +196 -0
- package/server/services/deployment/__init__.py +12 -1
- package/server/services/deployment/canary_registry.py +137 -0
- package/server/services/deployment/handlers.py +382 -0
- package/server/services/deployment/manager.py +653 -163
- package/server/services/deployment/poll_registry.py +2 -6
- package/server/services/deployment/state.py +2 -0
- package/server/services/deployment/triggers.py +87 -93
- package/server/services/event_waiter.py +47 -54
- package/server/services/events/__init__.py +11 -0
- package/server/services/events/admin_handlers.py +368 -0
- package/server/services/events/daemon.py +3 -1
- package/server/services/events/dispatch.py +188 -0
- package/server/services/events/envelope.py +264 -45
- package/server/services/events/oauth_lifecycle.py +98 -42
- package/server/services/events/triggers.py +3 -13
- package/server/services/events/verifiers/hmac_basic.py +1 -1
- package/server/services/events/verifiers/standard_webhooks.py +2 -4
- package/server/services/events/webhook.py +2 -3
- package/server/services/example_loader.py +73 -15
- package/server/services/execution/cache.py +36 -76
- package/server/services/execution/conditions.py +7 -20
- package/server/services/execution/dlq.py +20 -24
- package/server/services/execution/executor.py +234 -265
- package/server/services/execution/models.py +40 -46
- package/server/services/execution/recovery.py +23 -46
- package/server/services/handlers/__init__.py +12 -16
- package/server/services/handlers/todo.py +3 -6
- package/server/services/handlers/tools.py +143 -194
- package/server/services/handlers/triggers.py +24 -23
- package/server/services/llm/config.py +10 -1
- package/server/services/llm/factory.py +16 -4
- package/server/services/llm/messages.py +1 -5
- package/server/services/llm/protocol.py +9 -1
- package/server/services/llm/providers/anthropic.py +23 -12
- package/server/services/llm/providers/gemini.py +43 -22
- package/server/services/llm/providers/openai.py +14 -6
- package/server/services/llm/providers/openrouter.py +6 -1
- package/server/services/markdown_formatter.py +1 -2
- package/server/services/memory/__init__.py +2 -2
- package/server/services/memory/jsonl.py +6 -2
- package/server/services/memory/markdown.py +6 -6
- package/server/services/memory/state.py +6 -5
- package/server/services/memory_store.py +8 -12
- package/server/services/model_registry.py +22 -20
- package/server/services/node_executor.py +85 -80
- package/server/services/node_output_schemas.py +4 -7
- package/server/services/node_registry.py +40 -4
- package/server/services/node_spec.py +3 -7
- package/server/services/nodejs_client.py +4 -14
- package/server/services/oauth_utils.py +11 -7
- package/server/services/parameter_resolver.py +30 -36
- package/server/services/plugin/base.py +321 -38
- package/server/services/plugin/connection.py +12 -7
- package/server/services/plugin/credential.py +80 -22
- package/server/services/plugin/edge_walker.py +128 -105
- package/server/services/plugin/identifiers.py +48 -0
- package/server/services/plugin/interceptor.py +1 -1
- package/server/services/plugin/oauth.py +25 -21
- package/server/services/plugin/operation.py +1 -1
- package/server/services/plugin/polling.py +151 -26
- package/server/services/plugin/registry.py +52 -4
- package/server/services/plugin/routing.py +6 -9
- package/server/services/plugin/scaling.py +36 -18
- package/server/services/plugin/service_factories.py +95 -0
- package/server/services/plugin/shutdown_hooks.py +103 -0
- package/server/services/plugin/social_provider_registry.py +80 -0
- package/server/services/plugin/ws.py +2 -1
- package/server/services/pricing.py +26 -40
- package/server/services/pricing_handlers.py +90 -0
- package/server/services/process_service.py +33 -32
- package/server/services/proxy/models.py +15 -9
- package/server/services/proxy/service.py +26 -40
- package/server/services/rlm/adapters.py +43 -40
- package/server/services/rlm/constants.py +9 -9
- package/server/services/rlm/service.py +57 -45
- package/server/services/scheduler.py +8 -39
- package/server/services/settings/__init__.py +16 -0
- package/server/services/settings/handlers.py +275 -0
- package/server/services/skill_loader.py +53 -45
- package/server/services/skill_prompt.py +8 -6
- package/server/services/skills/__init__.py +23 -0
- package/server/services/skills/handlers.py +479 -0
- package/server/services/status_broadcaster.py +314 -291
- package/server/services/temporal/__init__.py +22 -1
- package/server/services/temporal/_handlers.py +65 -0
- package/server/services/temporal/_install.py +158 -0
- package/server/services/temporal/_refresh.py +57 -0
- package/server/services/temporal/_retry_policies.py +85 -0
- package/server/services/temporal/_runtime.py +181 -0
- package/server/services/temporal/_supervised_runtime.py +102 -0
- package/server/services/temporal/activities.py +168 -11
- package/server/services/temporal/agent_activities.py +683 -0
- package/server/services/temporal/agent_workflow.py +601 -0
- package/server/services/temporal/client.py +58 -13
- package/server/services/temporal/executor.py +2 -3
- package/server/services/temporal/plugin_activities.py +37 -2
- package/server/services/temporal/plugin_registry.py +82 -0
- package/server/services/temporal/polling_trigger_workflow.py +267 -0
- package/server/services/temporal/schedules.py +220 -0
- package/server/services/temporal/search_attributes.py +177 -0
- package/server/services/temporal/trigger_listener_workflow.py +378 -0
- package/server/services/temporal/worker.py +111 -18
- package/server/services/temporal/workflow.py +259 -40
- package/server/services/temporal/ws_client.py +22 -11
- package/server/services/text.py +14 -28
- package/server/services/tracked_http.py +29 -49
- package/server/services/user_auth.py +7 -21
- package/server/services/workflow.py +28 -20
- package/server/services/workflow_import.py +351 -0
- package/server/services/workflow_ops.py +4 -0
- package/server/services/workflow_storage/__init__.py +18 -0
- package/server/services/workflow_storage/handlers.py +132 -0
- package/server/services/workflow_validator.py +209 -0
- package/server/services/ws_handler_registry.py +80 -9
- package/server/skills/assistant/agent-builder-skill/SKILL.md +6 -6
- package/server/tests/conftest.py +54 -3
- package/server/tests/credentials/test_auth_service.py +9 -21
- package/server/tests/credentials/test_credential_broadcasts.py +116 -22
- package/server/tests/credentials/test_credentials_database.py +12 -38
- package/server/tests/credentials/test_encryption.py +3 -9
- package/server/tests/credentials/test_google_oauth.py +1 -3
- package/server/tests/credentials/test_oauth_utils.py +31 -38
- package/server/tests/credentials/test_twitter_oauth.py +1 -3
- package/server/tests/credentials/test_websocket_handlers.py +37 -72
- package/server/tests/fixtures/tool_names_snapshot.json +78 -0
- package/server/tests/llm/test_factory.py +12 -4
- package/server/tests/llm/test_providers.py +25 -32
- package/server/tests/llm/test_wiring.py +27 -22
- package/server/tests/nodes/_compat.py +4 -5
- package/server/tests/nodes/_harness.py +31 -24
- package/server/tests/nodes/_mocks.py +2 -6
- package/server/tests/nodes/test_agent_builder.py +43 -35
- package/server/tests/nodes/test_ai_agents.py +29 -24
- package/server/tests/nodes/test_ai_chat_models.py +3 -9
- package/server/tests/nodes/test_ai_tools.py +29 -24
- package/server/tests/nodes/test_android.py +34 -64
- package/server/tests/nodes/test_chat_utility.py +2 -2
- package/server/tests/nodes/test_code_fs_process.py +26 -84
- package/server/tests/nodes/test_document.py +23 -47
- package/server/tests/nodes/test_email.py +88 -51
- package/server/tests/nodes/test_google_workspace.py +26 -20
- package/server/tests/nodes/test_http_proxy.py +43 -89
- package/server/tests/nodes/test_search.py +3 -9
- package/server/tests/nodes/test_specialized_agents.py +58 -162
- package/server/tests/nodes/test_stripe_plugin.py +25 -5
- package/server/tests/nodes/test_telegram_social.py +33 -37
- package/server/tests/nodes/test_twitter.py +59 -150
- package/server/tests/nodes/test_web_automation.py +21 -51
- package/server/tests/nodes/test_whatsapp.py +13 -19
- package/server/tests/nodes/test_workflow_triggers.py +16 -45
- package/server/tests/services/cli_agent/test_claude_session_events.py +201 -0
- package/server/tests/services/cli_agent/test_jsonl_watcher.py +190 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +67 -29
- package/server/tests/services/cli_agent/test_providers.py +236 -47
- package/server/tests/services/cli_agent/test_service.py +9 -7
- package/server/tests/services/memory/test_jsonl.py +30 -25
- package/server/tests/services/test_events.py +26 -7
- package/server/tests/services/test_identifiers.py +122 -0
- package/server/tests/services/test_process_lifecycle.py +129 -0
- package/server/tests/services/test_supervisor.py +0 -1
- package/server/tests/temporal/__init__.py +0 -0
- package/server/tests/temporal/test_agent_workflow.py +215 -0
- package/server/tests/temporal/test_dispatch.py +231 -0
- package/server/tests/test_admin_handlers.py +394 -0
- package/server/tests/test_auto_skill.py +4 -2
- package/server/tests/test_canary_registry.py +310 -0
- package/server/tests/test_chat_trigger_canary_producer.py +101 -0
- package/server/tests/test_cloudevents_node_parameters.py +129 -0
- package/server/tests/test_credential_icon.py +115 -0
- package/server/tests/test_cron_canary.py +511 -0
- package/server/tests/test_deployment_canary_listener.py +692 -0
- package/server/tests/test_event_framework_phase_a.py +537 -0
- package/server/tests/test_no_raw_prints.py +131 -0
- package/server/tests/test_node_spec.py +196 -103
- package/server/tests/test_parameter_resolver.py +20 -20
- package/server/tests/test_plugin_contract.py +76 -49
- package/server/tests/test_plugin_helpers.py +0 -1
- package/server/tests/test_plugin_self_containment.py +40 -47
- package/server/tests/test_polling_trigger_workflow.py +572 -0
- package/server/tests/test_retry_policies.py +146 -0
- package/server/tests/test_service_factories.py +168 -0
- package/server/tests/test_shutdown_hooks.py +199 -0
- package/server/tests/test_social_provider_registry.py +177 -0
- package/server/tests/test_status_broadcasts.py +214 -63
- package/server/tests/test_task_trigger_canary_producer.py +131 -0
- package/server/tests/test_telegram_trigger_canary_producer.py +113 -0
- package/server/tests/test_tool_registry.py +110 -0
- package/server/tests/test_trigger_listener_workflow.py +365 -0
- package/server/tests/test_whatsapp_trigger_canary_producer.py +164 -0
- package/server/tests/test_workflow_ops.py +1 -3
- package/server/tests/test_workflow_validator.py +791 -0
- package/server/uv.lock +3539 -0
- package/client/dist/assets/index-DQ0nwhec.js +0 -257
- package/client/src/assets/icons/apify/index.ts +0 -19
- package/client/src/assets/icons/browser/index.ts +0 -17
- package/client/src/assets/icons/email/index.ts +0 -22
- package/client/src/assets/icons/google/index.ts +0 -34
- package/client/src/assets/icons/llm/deepseek.svg +0 -1
- package/client/src/assets/icons/llm/index.ts +0 -18
- package/client/src/assets/icons/llm/kimi.svg +0 -1
- package/client/src/assets/icons/llm/mistral.svg +0 -1
- package/client/src/assets/icons/search/index.ts +0 -28
- package/client/src/assets/icons/telegram/index.ts +0 -19
- package/machina/buildenv.py +0 -44
- package/machina/cli.py +0 -55
- package/machina/commands/__init__.py +0 -1
- package/machina/commands/clean.py +0 -80
- package/machina/commands/daemon.py +0 -150
- package/machina/config.py +0 -93
- package/machina/platform_.py +0 -37
- package/machina/pyproject.toml +0 -33
- package/server/nodes/agent/deep_agent.py +0 -103
- package/server/services/agents/__init__.py +0 -9
- package/server/services/agents/adapters.py +0 -199
- package/server/services/agents/constants.py +0 -10
- package/server/services/agents/service.py +0 -297
- package/server/services/cli_agent/providers/anthropic_claude.py +0 -419
- /package/{machina → cli}/README.md +0 -0
- /package/{machina → cli}/__init__.py +0 -0
- /package/{client/src/assets/icons/apify → server/credentials/icons}/apify.svg +0 -0
- /package/{client/src/assets/icons/search/brave.svg → server/credentials/icons/brave_search.svg} +0 -0
- /package/{client/src/assets/icons/email/read.svg → server/credentials/icons/email_himalaya.svg} +0 -0
- /package/{client/src/assets/icons/search → server/credentials/icons}/perplexity.svg +0 -0
- /package/{client/src/assets/icons/search/google.svg → server/credentials/icons/serper.svg} +0 -0
- /package/{client/src/assets → server/credentials}/icons/stripe.svg +0 -0
- /package/{client/src/assets/icons/twitter/x.svg → server/credentials/icons/twitter.svg} +0 -0
- /package/{client/src/assets/icons/browser/chrome.svg → server/nodes/browser/browser/icon.svg} +0 -0
- /package/{client/src/assets/icons/chat/chat.svg → server/nodes/chat/chat_history/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/javascript.svg → server/nodes/code/javascript_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/python.svg → server/nodes/code/python_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/typescript.svg → server/nodes/code/typescript_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/email/receive.svg → server/nodes/email/email_receive/icon.svg} +0 -0
- /package/{client/src/assets/icons/email/send.svg → server/nodes/email/email_send/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/calendar.svg → server/nodes/google/calendar/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/contacts.svg → server/nodes/google/contacts/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/drive.svg → server/nodes/google/drive/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/gmail.svg → server/nodes/google/gmail/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/sheets.svg → server/nodes/google/sheets/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/tasks.svg → server/nodes/google/tasks/icon.svg} +0 -0
- /package/{client/src/assets/icons/search/duckduckgo.svg → server/nodes/search/duckduckgo_search/icon.svg} +0 -0
- /package/{client/src/assets/icons/social/social.svg → server/nodes/social/social_receive/icon.svg} +0 -0
- /package/{client/src/assets/icons/telegram/telegram.svg → server/nodes/telegram/icon.svg} +0 -0
- /package/server/nodes/utility/{team_monitor.py → team_monitor/__init__.py} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-db.svg → server/nodes/whatsapp/icon_whatsappDb.svg} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-receive.svg → server/nodes/whatsapp/icon_whatsappReceive.svg} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-send.svg → server/nodes/whatsapp/icon_whatsappSend.svg} +0 -0
- /package/{client/src/assets/icons → server/nodes}/whatsapp/whatsapp.svg +0 -0
|
@@ -65,25 +65,29 @@ class TemporalClientWrapper:
|
|
|
65
65
|
)
|
|
66
66
|
# Verify namespace is ready (gRPC port may accept connections
|
|
67
67
|
# before the server finishes registering namespaces)
|
|
68
|
-
await client.service_client.workflow_service.describe_namespace(
|
|
69
|
-
DescribeNamespaceRequest(namespace=self.namespace)
|
|
70
|
-
)
|
|
68
|
+
await client.service_client.workflow_service.describe_namespace(DescribeNamespaceRequest(namespace=self.namespace))
|
|
71
69
|
self._client = client
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
logger.info(f"Connected to Temporal server at {self.server_address}")
|
|
71
|
+
# Wave 12 A4: idempotently register the event-framework
|
|
72
|
+
# Search Attributes. Failure here is non-fatal — the
|
|
73
|
+
# framework still works without them, dispatch.emit just
|
|
74
|
+
# falls back to broadcast-only routing instead of
|
|
75
|
+
# signalling consumers.
|
|
76
|
+
try:
|
|
77
|
+
from services.temporal.search_attributes import (
|
|
78
|
+
register_search_attributes,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
await register_search_attributes(self._client, self.namespace)
|
|
82
|
+
except Exception as sa_exc: # noqa: BLE001 — non-fatal
|
|
83
|
+
logger.warning(f"Search-attribute registration failed (non-fatal): {sa_exc}")
|
|
74
84
|
return self._client
|
|
75
85
|
except Exception as e:
|
|
76
|
-
|
|
77
|
-
logger.warning(
|
|
78
|
-
f"Temporal connection attempt {attempt}/{retries} failed: {e}"
|
|
79
|
-
)
|
|
86
|
+
logger.warning(f"Temporal connection attempt {attempt}/{retries} failed: {e}")
|
|
80
87
|
if attempt < retries:
|
|
81
88
|
await asyncio.sleep(delay)
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
logger.error(
|
|
85
|
-
f"Failed to connect to Temporal server at {self.server_address} after {retries} attempts"
|
|
86
|
-
)
|
|
90
|
+
logger.error(f"Failed to connect to Temporal server at {self.server_address} after {retries} attempts")
|
|
87
91
|
return None
|
|
88
92
|
|
|
89
93
|
async def disconnect(self) -> None:
|
|
@@ -91,3 +95,44 @@ class TemporalClientWrapper:
|
|
|
91
95
|
if self._client is not None:
|
|
92
96
|
self._client = None
|
|
93
97
|
logger.info("Disconnected from Temporal server")
|
|
98
|
+
|
|
99
|
+
async def terminate_running_workflows(
|
|
100
|
+
self,
|
|
101
|
+
*,
|
|
102
|
+
reason: str = "MachinaOS startup: auto-resumption disabled",
|
|
103
|
+
) -> int:
|
|
104
|
+
"""Terminate every workflow in ``Running`` state in our namespace.
|
|
105
|
+
|
|
106
|
+
Preserves history — terminated workflows remain visible in the
|
|
107
|
+
Temporal UI as ``Terminated``, only the active execution stops.
|
|
108
|
+
Run from ``main.py`` lifespan between client connect and worker
|
|
109
|
+
start so the worker doesn't accept activities from workflows
|
|
110
|
+
that are about to be terminated. Gated by
|
|
111
|
+
``Settings.temporal_terminate_running_on_startup`` at the call
|
|
112
|
+
site; this method always terminates when invoked.
|
|
113
|
+
|
|
114
|
+
Returns the count of workflows terminated. Failures on
|
|
115
|
+
individual workflows are logged but don't abort the sweep —
|
|
116
|
+
terminating a workflow that completed mid-query is a benign
|
|
117
|
+
race that produces ``WorkflowNotFoundError``.
|
|
118
|
+
"""
|
|
119
|
+
if self._client is None:
|
|
120
|
+
logger.warning("terminate_running_workflows called before connect; no-op")
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
count = 0
|
|
124
|
+
async for wf in self._client.list_workflows(
|
|
125
|
+
"ExecutionStatus = 'Running'",
|
|
126
|
+
):
|
|
127
|
+
try:
|
|
128
|
+
handle = self._client.get_workflow_handle(
|
|
129
|
+
wf.id,
|
|
130
|
+
run_id=wf.run_id,
|
|
131
|
+
)
|
|
132
|
+
await handle.terminate(reason=reason)
|
|
133
|
+
count += 1
|
|
134
|
+
except Exception as exc: # noqa: BLE001 — best-effort sweep
|
|
135
|
+
logger.debug(f"Failed to terminate workflow id={wf.id} " f"run_id={wf.run_id}: {exc}")
|
|
136
|
+
if count:
|
|
137
|
+
logger.info(f"Terminated {count} running workflow(s) at startup " "(history preserved; resumption disabled)")
|
|
138
|
+
return count
|
|
@@ -98,9 +98,7 @@ class TemporalExecutor:
|
|
|
98
98
|
result.get("outputs", {}).get(node_id, {}),
|
|
99
99
|
)
|
|
100
100
|
except Exception as e:
|
|
101
|
-
logger.warning(
|
|
102
|
-
f"Status callback error for node {node_id}: {e}"
|
|
103
|
-
)
|
|
101
|
+
logger.warning(f"Status callback error for node {node_id}: {e}")
|
|
104
102
|
|
|
105
103
|
logger.info(
|
|
106
104
|
"Temporal workflow completed",
|
|
@@ -124,6 +122,7 @@ class TemporalExecutor:
|
|
|
124
122
|
|
|
125
123
|
except Exception as e:
|
|
126
124
|
import traceback
|
|
125
|
+
|
|
127
126
|
execution_time = time.time() - start_time
|
|
128
127
|
error_details = f"{type(e).__name__}: {str(e)}"
|
|
129
128
|
tb = traceback.format_exc()
|
|
@@ -27,7 +27,7 @@ a specialised worker pool.
|
|
|
27
27
|
|
|
28
28
|
from __future__ import annotations
|
|
29
29
|
|
|
30
|
-
from typing import
|
|
30
|
+
from typing import Callable, Dict, Iterable, List, Optional
|
|
31
31
|
|
|
32
32
|
from core.logging import get_logger
|
|
33
33
|
|
|
@@ -69,7 +69,9 @@ def collect_plugin_activities(
|
|
|
69
69
|
activity_fn = cls.as_activity()
|
|
70
70
|
except Exception as e:
|
|
71
71
|
logger.warning(
|
|
72
|
-
"Failed to build activity for %s: %s",
|
|
72
|
+
"Failed to build activity for %s: %s",
|
|
73
|
+
node_type,
|
|
74
|
+
e,
|
|
73
75
|
)
|
|
74
76
|
continue
|
|
75
77
|
activities.append(activity_fn)
|
|
@@ -87,6 +89,39 @@ def collect_plugin_activities_for_queue(task_queue: str) -> List[Callable]:
|
|
|
87
89
|
return collect_plugin_activities(task_queue=task_queue)
|
|
88
90
|
|
|
89
91
|
|
|
92
|
+
def collect_polling_activities() -> List[Callable]:
|
|
93
|
+
"""Return ``@activity.defn``-decorated per-cycle activities for every
|
|
94
|
+
:class:`services.plugin.PollingTriggerNode` subclass.
|
|
95
|
+
|
|
96
|
+
Wave 12 C2: each polling plugin's
|
|
97
|
+
:meth:`PollingTriggerNode.as_poll_activity` produces an activity that
|
|
98
|
+
does ONE poll cycle (vs ``cls.as_activity()`` which does ONE workflow
|
|
99
|
+
execution). The new :class:`PollingTriggerWorkflow` calls these per
|
|
100
|
+
``workflow.sleep`` tick.
|
|
101
|
+
|
|
102
|
+
Stable activity name: ``poll.{type}.v{version}``. Registered alongside
|
|
103
|
+
the regular per-type activities in the Temporal worker.
|
|
104
|
+
"""
|
|
105
|
+
from services.node_registry import registered_node_classes
|
|
106
|
+
from services.plugin import PollingTriggerNode
|
|
107
|
+
|
|
108
|
+
activities: List[Callable] = []
|
|
109
|
+
for node_type, cls in registered_node_classes().items():
|
|
110
|
+
if not isinstance(cls, type) or not issubclass(cls, PollingTriggerNode):
|
|
111
|
+
continue
|
|
112
|
+
try:
|
|
113
|
+
activities.append(cls.as_poll_activity())
|
|
114
|
+
except Exception as exc: # noqa: BLE001
|
|
115
|
+
logger.warning(
|
|
116
|
+
"Failed to build poll activity for %s: %s",
|
|
117
|
+
node_type,
|
|
118
|
+
exc,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
logger.info("Collected %d polling activities", len(activities))
|
|
122
|
+
return activities
|
|
123
|
+
|
|
124
|
+
|
|
90
125
|
def distinct_task_queues() -> List[str]:
|
|
91
126
|
"""Return every task_queue string declared by any registered plugin.
|
|
92
127
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Plugin registry for Temporal SDK ``Worker(plugins=[...])``.
|
|
2
|
+
|
|
3
|
+
Plugins that ship workflow classes / activities / interceptors build a
|
|
4
|
+
:class:`temporalio.plugin.SimplePlugin` instance and register it from
|
|
5
|
+
their ``__init__.py``. The Temporal worker passes the snapshot to
|
|
6
|
+
``Worker(plugins=[...])`` once at startup; the Temporal SDK's
|
|
7
|
+
chain-of-responsibility ``configure_worker()`` hook merges each plugin's
|
|
8
|
+
contributions into the worker's effective workflow / activity list
|
|
9
|
+
internally.
|
|
10
|
+
|
|
11
|
+
Same mechanism as the Temporal SDK's own contrib plugins
|
|
12
|
+
(``temporalio.contrib.openai_agents`` ships ``OpenAIAgentsPlugin`` for
|
|
13
|
+
exactly this purpose). Documented at
|
|
14
|
+
https://python.temporal.io/temporalio.plugin.SimplePlugin.html and
|
|
15
|
+
https://docs.temporal.io/develop/plugins-guide .
|
|
16
|
+
|
|
17
|
+
Why a registry rather than direct framework imports
|
|
18
|
+
---------------------------------------------------
|
|
19
|
+
|
|
20
|
+
The framework worker stays plugin-agnostic — no
|
|
21
|
+
``from nodes.scheduler... import CronTriggerWorkflow`` at the framework
|
|
22
|
+
level. Plugins push their ``SimplePlugin`` instances in; the worker
|
|
23
|
+
reads the list and hands it to Temporal. Same direction-of-dependency
|
|
24
|
+
contract the eight other Wave-11.I self-registration registries enforce
|
|
25
|
+
(canary_trigger_type / poll_coroutine_factory / filter_builder /
|
|
26
|
+
ws_handler / router / oauth_callback_path / service_refresh /
|
|
27
|
+
output_schema / shutdown_hook / service_factory / social_send_handler).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from typing import List
|
|
33
|
+
|
|
34
|
+
from temporalio.plugin import SimplePlugin
|
|
35
|
+
|
|
36
|
+
from services.plugin.registry import IdempotentRegistry
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Keyed by plugin name so duplicates raise at import time (e.g. two
|
|
40
|
+
# plugins both declaring ``name="cron-scheduler"`` would surface a
|
|
41
|
+
# namespace clash). SimplePlugin instances are content-comparable via
|
|
42
|
+
# the IdempotentRegistry's qualname check (reload safe).
|
|
43
|
+
_REGISTRY: IdempotentRegistry[str, SimplePlugin] = IdempotentRegistry("temporal_plugin")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def register_temporal_plugin(plugin: SimplePlugin) -> None:
|
|
47
|
+
"""Publish a Temporal ``SimplePlugin`` for the worker.
|
|
48
|
+
|
|
49
|
+
Idempotent on re-import. Raises ``ValueError`` on a namespace
|
|
50
|
+
conflict (same ``plugin.name`` registered by a different caller).
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
plugin: A :class:`temporalio.plugin.SimplePlugin` instance.
|
|
54
|
+
The ``name`` field is used as the registry key; ``workflows``,
|
|
55
|
+
``activities``, ``interceptors`` etc. surface in the
|
|
56
|
+
worker's effective configuration via Temporal's plugin chain.
|
|
57
|
+
"""
|
|
58
|
+
# ``SimplePlugin.name`` is a method (per temporalio.plugin API) — call
|
|
59
|
+
# it to get the actual identifier string the registry keys on.
|
|
60
|
+
_REGISTRY.register(plugin.name(), plugin)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def temporal_plugins() -> List[SimplePlugin]:
|
|
64
|
+
"""Return every registered plugin (snapshot, in registration order).
|
|
65
|
+
|
|
66
|
+
The Temporal worker reads this once at startup::
|
|
67
|
+
|
|
68
|
+
worker = Worker(
|
|
69
|
+
client,
|
|
70
|
+
task_queue=...,
|
|
71
|
+
plugins=temporal_plugins(),
|
|
72
|
+
workflows=[MachinaWorkflow, ...],
|
|
73
|
+
activities=[...],
|
|
74
|
+
)
|
|
75
|
+
"""
|
|
76
|
+
return list(_REGISTRY.values())
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
__all__ = [
|
|
80
|
+
"register_temporal_plugin",
|
|
81
|
+
"temporal_plugins",
|
|
82
|
+
]
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""Wave 12 C2: long-lived Temporal workflow for polling triggers.
|
|
2
|
+
|
|
3
|
+
Closes the durability gap on polling triggers (gmailReceive,
|
|
4
|
+
twitterReceive, …) the same way :class:`TriggerListenerWorkflow`
|
|
5
|
+
closed it for push triggers. Today's polling lives in
|
|
6
|
+
``services/deployment/triggers.py::setup_polling_trigger`` —
|
|
7
|
+
a collector/processor ``asyncio.Task`` pair that dies on FastAPI
|
|
8
|
+
restart, losing the seen-ID baseline and any in-flight cycle.
|
|
9
|
+
|
|
10
|
+
This workflow owns the poll loop INSIDE Temporal:
|
|
11
|
+
|
|
12
|
+
- ``workflow.sleep(interval)`` between cycles → replayable, no
|
|
13
|
+
per-cycle heartbeat overhead, survives worker restarts
|
|
14
|
+
(per temporal.io/blog/very-long-running-workflows).
|
|
15
|
+
- Per-cycle Temporal activity (``poll.{type}.v{version}``, emitted
|
|
16
|
+
by :meth:`PollingTriggerNode.as_poll_activity`) does ONE cycle and
|
|
17
|
+
returns new events + the updated seen-id set.
|
|
18
|
+
- For each new event, spawn a child :class:`MachinaWorkflow` with
|
|
19
|
+
the trigger pre-executed (same ``_build_run_graph`` helper as
|
|
20
|
+
the push-trigger listener — single source of truth for run-filter
|
|
21
|
+
semantics).
|
|
22
|
+
- ``continueAsNew`` every ~16K processed events to keep Event
|
|
23
|
+
History bounded.
|
|
24
|
+
|
|
25
|
+
Cross-confirmed pattern with Temporal docs + samples-python:
|
|
26
|
+
- Long-lived workflow with sleep+continueAsNew: temporal.io/blog/
|
|
27
|
+
very-long-running-workflows
|
|
28
|
+
- Activities for external I/O (Gmail / Twitter API calls): docs.
|
|
29
|
+
temporal.io/develop/python/core-application#activities
|
|
30
|
+
- ParentClosePolicy.ABANDON for child run workflows so listener
|
|
31
|
+
cancel never strands in-flight executions
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
from datetime import timedelta
|
|
37
|
+
from typing import Any, Dict, Set
|
|
38
|
+
|
|
39
|
+
from temporalio import workflow
|
|
40
|
+
from temporalio.common import WorkflowIDReusePolicy
|
|
41
|
+
from temporalio.workflow import ParentClosePolicy
|
|
42
|
+
|
|
43
|
+
from ._retry_policies import DEFAULT_ACTIVITY_RETRY
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Same continueAsNew threshold as TriggerListenerWorkflow — keeps Event
|
|
47
|
+
# History under Temporal's soft ceiling. The actual count depends on
|
|
48
|
+
# both cycles AND events spawned per cycle; 16K events ≈ 16K spawn
|
|
49
|
+
# entries + N cycles, well below the 50K guidance.
|
|
50
|
+
_MAX_EVENTS_BEFORE_CONTINUE_AS_NEW = 16_000
|
|
51
|
+
|
|
52
|
+
# Default poll interval (seconds) if listener_data doesn't supply one.
|
|
53
|
+
# Mirrors PollingTriggerNode.default_poll_interval defaults.
|
|
54
|
+
_DEFAULT_POLL_INTERVAL_S = 60
|
|
55
|
+
|
|
56
|
+
# Activity timeout: 4× the poll interval gives the activity plenty of
|
|
57
|
+
# headroom for slow Gmail / Twitter responses without hanging forever.
|
|
58
|
+
# Workflow ``RetryPolicy`` (default) handles transient failures.
|
|
59
|
+
_ACTIVITY_TIMEOUT_MULT = 4
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@workflow.defn(name="PollingTriggerWorkflow", sandboxed=False)
|
|
63
|
+
class PollingTriggerWorkflow:
|
|
64
|
+
"""Long-lived polling-trigger workflow.
|
|
65
|
+
|
|
66
|
+
Determinism note: all state lives on ``self`` and is reconstructed
|
|
67
|
+
from Event History on replay. Provider-side ``seen_ids`` (e.g.
|
|
68
|
+
Gmail message IDs) is the activity's output → workflow state →
|
|
69
|
+
next activity's input — never mutated mid-workflow. Event-id dedup
|
|
70
|
+
set drains on ``continueAsNew`` (intentional; events arriving in
|
|
71
|
+
the new run are by definition not duplicates of the prior run).
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self) -> None:
|
|
75
|
+
self._seen_event_ids: Set[str] = set()
|
|
76
|
+
self._processed_count: int = 0
|
|
77
|
+
|
|
78
|
+
@workflow.run
|
|
79
|
+
async def run(self, listener_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
80
|
+
"""Poll loop body.
|
|
81
|
+
|
|
82
|
+
``listener_data`` shape (deployment-supplied)::
|
|
83
|
+
|
|
84
|
+
{
|
|
85
|
+
"workflow_id": str, # MachinaOs deployment workflow_id
|
|
86
|
+
"trigger_node_id": str, # node id that fires on each event
|
|
87
|
+
"node_type": str, # e.g. "googleGmailReceive"
|
|
88
|
+
"version": int, # plugin class version (for activity name)
|
|
89
|
+
"filter_params": Dict, # plugin params (poll_interval etc.)
|
|
90
|
+
"nodes": List[Dict], # full deployment graph snapshot
|
|
91
|
+
"edges": List[Dict],
|
|
92
|
+
"session_id": str,
|
|
93
|
+
"tenant_id": Optional[str],
|
|
94
|
+
"seen_ids": List[str], # carried across continueAsNew; empty on first start
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Returns when ``continueAsNew`` fires. Deployment cancel uses
|
|
98
|
+
a graceful ``workflow.cancel()`` — the loop's
|
|
99
|
+
``CancelledError`` propagates and the workflow ends.
|
|
100
|
+
"""
|
|
101
|
+
node_type = listener_data["node_type"]
|
|
102
|
+
version = listener_data.get("version", 1)
|
|
103
|
+
activity_name = f"poll.{node_type}.v{version}"
|
|
104
|
+
|
|
105
|
+
params = listener_data.get("filter_params", {}) or {}
|
|
106
|
+
poll_interval = int(params.get("poll_interval") or _DEFAULT_POLL_INTERVAL_S)
|
|
107
|
+
activity_timeout_s = max(30, poll_interval * _ACTIVITY_TIMEOUT_MULT)
|
|
108
|
+
|
|
109
|
+
# Carry seen_ids across continueAsNew. First-start payload has
|
|
110
|
+
# ``seen_ids=[]`` and the first activity call is baseline-only.
|
|
111
|
+
seen_ids: Set[str] = set(listener_data.get("seen_ids") or [])
|
|
112
|
+
is_baseline = not seen_ids
|
|
113
|
+
|
|
114
|
+
workflow.logger.info(
|
|
115
|
+
f"PollingTriggerWorkflow started: workflow_id={listener_data.get('workflow_id')} "
|
|
116
|
+
f"node={listener_data.get('trigger_node_id')} type={node_type} "
|
|
117
|
+
f"interval={poll_interval}s baseline={is_baseline}"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
while True:
|
|
121
|
+
if is_baseline:
|
|
122
|
+
# Establish seen baseline immediately on first run so we
|
|
123
|
+
# don't re-emit items the user has had since before deploy.
|
|
124
|
+
is_baseline = False
|
|
125
|
+
cycle_payload = {
|
|
126
|
+
"node_id": listener_data["trigger_node_id"],
|
|
127
|
+
"params": params,
|
|
128
|
+
"seen_ids": [],
|
|
129
|
+
"baseline_only": True,
|
|
130
|
+
}
|
|
131
|
+
else:
|
|
132
|
+
await workflow.sleep(timedelta(seconds=poll_interval))
|
|
133
|
+
cycle_payload = {
|
|
134
|
+
"node_id": listener_data["trigger_node_id"],
|
|
135
|
+
"params": params,
|
|
136
|
+
"seen_ids": list(seen_ids),
|
|
137
|
+
"baseline_only": False,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
# Wave 12 D1: explicit RetryPolicy with
|
|
142
|
+
# non_retryable_error_types=("NodeUserError", ...) —
|
|
143
|
+
# poll-cycle failures from user-correctable causes
|
|
144
|
+
# (bad filter expression, missing credential) fail fast
|
|
145
|
+
# instead of burning 3 retries per cycle.
|
|
146
|
+
result = await workflow.execute_activity(
|
|
147
|
+
activity_name,
|
|
148
|
+
cycle_payload,
|
|
149
|
+
start_to_close_timeout=timedelta(seconds=activity_timeout_s),
|
|
150
|
+
retry_policy=DEFAULT_ACTIVITY_RETRY,
|
|
151
|
+
)
|
|
152
|
+
except Exception as exc: # noqa: BLE001
|
|
153
|
+
# Activity exhausted its RetryPolicy. Log + continue —
|
|
154
|
+
# don't terminate the listener over one bad cycle.
|
|
155
|
+
# Workflow Event History records the failure for ops.
|
|
156
|
+
workflow.logger.error(f"PollingTriggerWorkflow cycle failed (will retry next interval): {exc}")
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
seen_ids = set(result.get("seen_ids") or [])
|
|
160
|
+
events = result.get("events") or []
|
|
161
|
+
|
|
162
|
+
for event in events:
|
|
163
|
+
event_id = event.get("id")
|
|
164
|
+
if not event_id or event_id in self._seen_event_ids:
|
|
165
|
+
continue
|
|
166
|
+
self._seen_event_ids.add(event_id)
|
|
167
|
+
try:
|
|
168
|
+
await self._spawn_child_run(event, listener_data)
|
|
169
|
+
except Exception as spawn_exc: # noqa: BLE001
|
|
170
|
+
# Per-event spawn failure logged; subsequent events
|
|
171
|
+
# still try. Same isolation contract as the push
|
|
172
|
+
# listener.
|
|
173
|
+
workflow.logger.error(f"PollingTriggerWorkflow spawn failed for event.id={event_id}: {spawn_exc}")
|
|
174
|
+
self._processed_count += 1
|
|
175
|
+
|
|
176
|
+
if self._processed_count >= _MAX_EVENTS_BEFORE_CONTINUE_AS_NEW:
|
|
177
|
+
workflow.logger.info(f"PollingTriggerWorkflow continue_as_new: processed={self._processed_count}")
|
|
178
|
+
# Carry seen_ids forward so the new run doesn't re-emit
|
|
179
|
+
# what's already been seen by the provider.
|
|
180
|
+
listener_data["seen_ids"] = list(seen_ids)
|
|
181
|
+
workflow.continue_as_new(listener_data)
|
|
182
|
+
|
|
183
|
+
async def _spawn_child_run(
|
|
184
|
+
self,
|
|
185
|
+
event: Dict[str, Any],
|
|
186
|
+
listener_data: Dict[str, Any],
|
|
187
|
+
) -> None:
|
|
188
|
+
"""Start a child :class:`MachinaWorkflow` with the trigger
|
|
189
|
+
pre-executed against this event payload.
|
|
190
|
+
|
|
191
|
+
Reuses ``_build_run_graph`` from
|
|
192
|
+
:mod:`services.temporal.trigger_listener_workflow` so the
|
|
193
|
+
filtered-graph semantics (n8n stop-at-trigger downstream walk,
|
|
194
|
+
config nodes via input handles, toolkit sub-nodes, agent tool
|
|
195
|
+
nodes) stay single-source. Mirrors
|
|
196
|
+
:meth:`TriggerListenerWorkflow._spawn_child_run` exactly.
|
|
197
|
+
"""
|
|
198
|
+
from services.temporal.trigger_listener_workflow import (
|
|
199
|
+
_broadcast_trigger_idle,
|
|
200
|
+
_broadcast_trigger_waiting,
|
|
201
|
+
_build_run_graph,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
trigger_node_id = listener_data["trigger_node_id"]
|
|
205
|
+
nodes = listener_data["nodes"]
|
|
206
|
+
edges = listener_data["edges"]
|
|
207
|
+
session_id = listener_data.get("session_id", "default")
|
|
208
|
+
workflow_id = listener_data.get("workflow_id")
|
|
209
|
+
tenant_id = listener_data.get("tenant_id")
|
|
210
|
+
|
|
211
|
+
# Polling activity returns plugin-native payload dicts (Gmail
|
|
212
|
+
# email envelope, Twitter tweet payload). For Temporal-side
|
|
213
|
+
# introspection we pass the dict as both the trigger output
|
|
214
|
+
# AND nest it under ``_event_envelope`` so downstream nodes
|
|
215
|
+
# can route off the original shape — matches the push-listener
|
|
216
|
+
# contract.
|
|
217
|
+
trigger_output = {**event, "_event_envelope": event}
|
|
218
|
+
|
|
219
|
+
filtered_nodes, filtered_edges = _build_run_graph(
|
|
220
|
+
trigger_node_id=trigger_node_id,
|
|
221
|
+
trigger_output=trigger_output,
|
|
222
|
+
nodes=nodes,
|
|
223
|
+
edges=edges,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Lazy fallback to workflow.uuid4() only when event.id is missing —
|
|
227
|
+
# eager-eval default-arg form would trip _NotInWorkflowEventLoopError
|
|
228
|
+
# in unit tests + waste entropy on the hot path.
|
|
229
|
+
event_id = event.get("id") or workflow.uuid4().hex
|
|
230
|
+
child_id = f"run-{workflow_id}-{event_id}"
|
|
231
|
+
|
|
232
|
+
await _broadcast_trigger_idle(
|
|
233
|
+
node_id=trigger_node_id,
|
|
234
|
+
workflow_id=workflow_id,
|
|
235
|
+
event_id=event_id,
|
|
236
|
+
event_type=listener_data.get("event_type", ""),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
await workflow.start_child_workflow(
|
|
240
|
+
"MachinaWorkflow",
|
|
241
|
+
args=[
|
|
242
|
+
{
|
|
243
|
+
"nodes": filtered_nodes,
|
|
244
|
+
"edges": filtered_edges,
|
|
245
|
+
"session_id": session_id,
|
|
246
|
+
"workflow_id": workflow_id,
|
|
247
|
+
"tenant_id": tenant_id,
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
id=child_id,
|
|
251
|
+
parent_close_policy=ParentClosePolicy.ABANDON,
|
|
252
|
+
id_reuse_policy=WorkflowIDReusePolicy.ALLOW_DUPLICATE_FAILED_ONLY,
|
|
253
|
+
execution_timeout=timedelta(hours=1),
|
|
254
|
+
run_timeout=timedelta(hours=1),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
workflow.logger.info(f"PollingTriggerWorkflow spawned child run: child_id={child_id} " f"event.id={event.get('id')}")
|
|
258
|
+
|
|
259
|
+
await _broadcast_trigger_waiting(
|
|
260
|
+
node_id=trigger_node_id,
|
|
261
|
+
workflow_id=workflow_id,
|
|
262
|
+
event_type=listener_data.get("event_type", ""),
|
|
263
|
+
processed_count=self._processed_count + 1,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
__all__ = ["PollingTriggerWorkflow"]
|