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
|
@@ -62,9 +62,7 @@ class ProbeResult:
|
|
|
62
62
|
extra: Dict[str, Any] = field(default_factory=dict)
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
def classify_credential_error(
|
|
66
|
-
exc: BaseException, *, display_name: str
|
|
67
|
-
) -> ProbeResult:
|
|
65
|
+
def classify_credential_error(exc: BaseException, *, display_name: str) -> ProbeResult:
|
|
68
66
|
"""Map a transport / SDK exception to a typed ``ProbeResult``.
|
|
69
67
|
|
|
70
68
|
Single source of truth for credential-error → user-message mapping.
|
|
@@ -169,10 +167,7 @@ class Credential:
|
|
|
169
167
|
# Idempotent on re-import — only warn on genuine conflict.
|
|
170
168
|
existing = CREDENTIAL_REGISTRY[cls.id]
|
|
171
169
|
if existing.__qualname__ != cls.__qualname__:
|
|
172
|
-
raise ValueError(
|
|
173
|
-
f"Credential id '{cls.id}' registered by "
|
|
174
|
-
f"{existing.__qualname__} and now by {cls.__qualname__}"
|
|
175
|
-
)
|
|
170
|
+
raise ValueError(f"Credential id '{cls.id}' registered by " f"{existing.__qualname__} and now by {cls.__qualname__}")
|
|
176
171
|
CREDENTIAL_REGISTRY[cls.id] = cls
|
|
177
172
|
|
|
178
173
|
@classmethod
|
|
@@ -183,6 +178,63 @@ class Credential:
|
|
|
183
178
|
"""
|
|
184
179
|
raise NotImplementedError
|
|
185
180
|
|
|
181
|
+
# ---- F7: credential icon resolution -----------------------------
|
|
182
|
+
#
|
|
183
|
+
# Mirrors :func:`nodes._visuals.get_plugin_icon_path` for credentials.
|
|
184
|
+
# The frontend credential modal reads ``cls.icon`` strings of the
|
|
185
|
+
# form ``"asset:<key>"`` / ``"lobehub:<brand>"`` / emoji, OR fetches
|
|
186
|
+
# the SVG from the backend via ``GET /api/schemas/credentials/{provider}/icon``.
|
|
187
|
+
#
|
|
188
|
+
# Plugin-folder canonical convention (brand-name files):
|
|
189
|
+
# ``server/nodes/<plugin>/<cls.id>.svg``
|
|
190
|
+
#
|
|
191
|
+
# The credential icon lives directly inside its owner plugin's
|
|
192
|
+
# folder, named after the credential's brand (e.g.
|
|
193
|
+
# ``server/nodes/telegram/telegram.svg``). Distinguished from node
|
|
194
|
+
# icons by the absence of the ``icon`` / ``icon_<type>`` prefix —
|
|
195
|
+
# node icons follow that convention (see
|
|
196
|
+
# ``nodes/_visuals.py::get_plugin_icon_path``), credential icons
|
|
197
|
+
# don't. No ambiguity.
|
|
198
|
+
#
|
|
199
|
+
# Resolution order:
|
|
200
|
+
# 1. ``<plugin_folder>/<cls.id>.svg`` — co-located brand-name file.
|
|
201
|
+
# 2. ``server/credentials/icons/<cls.id>.svg`` — legacy central
|
|
202
|
+
# catalogue (pre-Wave 11.K; new credentials should ship the icon
|
|
203
|
+
# inside their plugin folder).
|
|
204
|
+
# 3. ``None`` — no backend icon; frontend falls back to ``cls.icon``.
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def get_icon_path(cls) -> Optional["Path"]:
|
|
208
|
+
"""Return the on-disk path to this credential's icon SVG, or None.
|
|
209
|
+
|
|
210
|
+
Lazy import of ``inspect`` + ``pathlib`` keeps the credential
|
|
211
|
+
module's import cost flat — both are stdlib so it's free.
|
|
212
|
+
"""
|
|
213
|
+
from pathlib import Path as _Path
|
|
214
|
+
import inspect
|
|
215
|
+
|
|
216
|
+
if not cls.id:
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
# 1. Co-located in the plugin folder, brand-name basename.
|
|
220
|
+
try:
|
|
221
|
+
cred_file = _Path(inspect.getfile(cls)).resolve()
|
|
222
|
+
except (TypeError, OSError):
|
|
223
|
+
cred_file = None
|
|
224
|
+
if cred_file is not None:
|
|
225
|
+
co_located = cred_file.parent / f"{cls.id}.svg"
|
|
226
|
+
if co_located.exists():
|
|
227
|
+
return co_located
|
|
228
|
+
|
|
229
|
+
# 2. Legacy central catalogue. ``server/`` resolves from this
|
|
230
|
+
# file: services/plugin/credential.py -> services/.. -> server/.
|
|
231
|
+
server_root = _Path(__file__).resolve().parent.parent.parent
|
|
232
|
+
central = server_root / "credentials" / "icons" / f"{cls.id}.svg"
|
|
233
|
+
if central.exists():
|
|
234
|
+
return central
|
|
235
|
+
|
|
236
|
+
return None
|
|
237
|
+
|
|
186
238
|
@classmethod
|
|
187
239
|
def inject(cls, secrets: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
|
|
188
240
|
"""Mutate an httpx request dict (headers / params / json / auth)
|
|
@@ -286,9 +338,7 @@ class Credential:
|
|
|
286
338
|
``openai.OpenAIError`` to let the base map them to a typed
|
|
287
339
|
message via :func:`classify_credential_error`.
|
|
288
340
|
"""
|
|
289
|
-
raise NotImplementedError(
|
|
290
|
-
f"Credential subclass {cls.__name__} must override _probe()"
|
|
291
|
-
)
|
|
341
|
+
raise NotImplementedError(f"Credential subclass {cls.__name__} must override _probe()")
|
|
292
342
|
|
|
293
343
|
|
|
294
344
|
class OAuth2Credential(Credential):
|
|
@@ -307,8 +357,8 @@ class OAuth2Credential(Credential):
|
|
|
307
357
|
token_header: ClassVar[str] = "Authorization"
|
|
308
358
|
token_prefix: ClassVar[str] = "Bearer "
|
|
309
359
|
# Keys the user enters in credentials modal (API-key style rows)
|
|
310
|
-
client_id_api_key: ClassVar[str] = ""
|
|
311
|
-
client_secret_api_key: ClassVar[str] = ""
|
|
360
|
+
client_id_api_key: ClassVar[str] = "" # e.g. "google_client_id"
|
|
361
|
+
client_secret_api_key: ClassVar[str] = "" # e.g. "google_client_secret"
|
|
312
362
|
|
|
313
363
|
@classmethod
|
|
314
364
|
async def resolve(cls, *, user_id: str = "owner") -> Dict[str, Any]:
|
|
@@ -317,9 +367,16 @@ class OAuth2Credential(Credential):
|
|
|
317
367
|
auth_service = container.auth_service()
|
|
318
368
|
tokens = await auth_service.get_oauth_tokens(cls.id, user_id)
|
|
319
369
|
if not tokens or not tokens.get("access_token"):
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
)
|
|
370
|
+
# Annotate the exception so BaseNode.execute can surface the
|
|
371
|
+
# failing provider in the response envelope and emit a
|
|
372
|
+
# CloudEvents-typed broadcast (credential.oauth.runtime_failed)
|
|
373
|
+
# via broadcast_credential_event. Plain attribute assignment —
|
|
374
|
+
# no new exception class.
|
|
375
|
+
err = PermissionError(f"No OAuth tokens for '{cls.id}'. Connect via Credentials modal.")
|
|
376
|
+
err.provider = cls.id
|
|
377
|
+
err.reason = "missing"
|
|
378
|
+
err.auth = cls.auth # "oauth2"
|
|
379
|
+
raise err
|
|
323
380
|
return tokens
|
|
324
381
|
|
|
325
382
|
@classmethod
|
|
@@ -360,7 +417,7 @@ class ApiKeyCredential(Credential):
|
|
|
360
417
|
|
|
361
418
|
auth: ClassVar[Literal["api_key"]] = "api_key"
|
|
362
419
|
# Where the key goes
|
|
363
|
-
key_name: ClassVar[str] = ""
|
|
420
|
+
key_name: ClassVar[str] = "" # header name or query-string key
|
|
364
421
|
key_location: ClassVar[Literal["header", "query", "bearer"]] = "header"
|
|
365
422
|
# Extra fields stored alongside (e.g. "apify_account_id" for Apify)
|
|
366
423
|
extra_fields: ClassVar[Sequence[str]] = ()
|
|
@@ -373,7 +430,7 @@ class ApiKeyCredential(Credential):
|
|
|
373
430
|
# ``httpx.HTTPStatusError`` / ``TimeoutException`` / ``ConnectError``
|
|
374
431
|
# propagate to :meth:`Credential.validate`, where
|
|
375
432
|
# :func:`classify_credential_error` produces the user-facing message.
|
|
376
|
-
probe_url: ClassVar[str] = ""
|
|
433
|
+
probe_url: ClassVar[str] = "" # set to enable the default probe
|
|
377
434
|
probe_method: ClassVar[str] = "GET"
|
|
378
435
|
probe_params: ClassVar[Dict[str, Any]] = {}
|
|
379
436
|
probe_json: ClassVar[Optional[Dict[str, Any]]] = None
|
|
@@ -386,9 +443,11 @@ class ApiKeyCredential(Credential):
|
|
|
386
443
|
auth_service = container.auth_service()
|
|
387
444
|
api_key = await auth_service.get_api_key(cls.id)
|
|
388
445
|
if not api_key:
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
446
|
+
err = PermissionError(f"No API key for '{cls.id}'. Add via Credentials modal.")
|
|
447
|
+
err.provider = cls.id
|
|
448
|
+
err.reason = "missing"
|
|
449
|
+
err.auth = cls.auth # "api_key"
|
|
450
|
+
raise err
|
|
392
451
|
secrets: Dict[str, Any] = {"api_key": api_key}
|
|
393
452
|
for field in cls.extra_fields:
|
|
394
453
|
value = await auth_service.get_api_key(field)
|
|
@@ -427,8 +486,7 @@ class ApiKeyCredential(Credential):
|
|
|
427
486
|
"""
|
|
428
487
|
if not cls.probe_url:
|
|
429
488
|
raise NotImplementedError(
|
|
430
|
-
f"Credential subclass {cls.__name__} must override _probe() "
|
|
431
|
-
f"or set the `probe_url` class attribute"
|
|
489
|
+
f"Credential subclass {cls.__name__} must override _probe() " f"or set the `probe_url` class attribute"
|
|
432
490
|
)
|
|
433
491
|
|
|
434
492
|
request = cls.inject(
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Pure functions extracted from ``services/handlers/ai.py`` so every
|
|
4
4
|
agent plugin can call them without depending on the legacy handler
|
|
5
5
|
module. Used by :class:`AIAgentNode`, :class:`ChatAgentNode`,
|
|
6
|
-
:class:`SpecializedAgentBase`, :class:`
|
|
7
|
-
:class:`
|
|
6
|
+
:class:`SpecializedAgentBase`, :class:`RLMAgentNode`,
|
|
7
|
+
:class:`ClaudeCodeAgentNode`.
|
|
8
8
|
|
|
9
9
|
Three helpers:
|
|
10
10
|
|
|
@@ -14,18 +14,26 @@ Three helpers:
|
|
|
14
14
|
``masterSkill`` expansion + the ``androidTool`` Sub-Node pattern +
|
|
15
15
|
child-agent tool discovery.
|
|
16
16
|
- :func:`collect_teammate_connections` — walks ``input-teammates``
|
|
17
|
-
edges (orchestrator / ai_employee
|
|
17
|
+
edges (orchestrator / ai_employee team-lead pattern).
|
|
18
18
|
- :func:`format_task_context` — renders ``taskTrigger`` payload as a
|
|
19
19
|
prompt prepend block.
|
|
20
20
|
|
|
21
|
+
Wave 11.I, X3: the Master-Skill expansion logic moved out of this
|
|
22
|
+
module (it used to ``from services.skill_loader import get_skill_loader``
|
|
23
|
+
inline). The :mod:`nodes.skill` package registers an expander callback
|
|
24
|
+
via :func:`register_master_skill_expander` from its ``__init__.py``;
|
|
25
|
+
edge_walker calls the registered callback instead of importing
|
|
26
|
+
``skill_loader`` directly.
|
|
27
|
+
|
|
21
28
|
These functions have **no service dependencies** beyond the
|
|
22
|
-
``Database`` instance
|
|
23
|
-
old ``handlers/ai.py:_collect_*``
|
|
29
|
+
``Database`` instance and the registered expander callback. They
|
|
30
|
+
mutate nothing. Wave 11.D.6 inlines the old ``handlers/ai.py:_collect_*``
|
|
31
|
+
shims to call here directly.
|
|
24
32
|
"""
|
|
25
33
|
|
|
26
34
|
from __future__ import annotations
|
|
27
35
|
|
|
28
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
|
36
|
+
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Tuple
|
|
29
37
|
|
|
30
38
|
from constants import AI_AGENT_TYPES, ANDROID_SERVICE_NODE_TYPES
|
|
31
39
|
from core.logging import get_logger
|
|
@@ -36,17 +44,52 @@ if TYPE_CHECKING:
|
|
|
36
44
|
logger = get_logger(__name__)
|
|
37
45
|
|
|
38
46
|
|
|
47
|
+
# ---- Master-Skill expander callback registry ----------------------------
|
|
48
|
+
|
|
49
|
+
MasterSkillExpander = Callable[
|
|
50
|
+
[str, Dict[str, Any]],
|
|
51
|
+
Awaitable[List[Dict[str, Any]]],
|
|
52
|
+
]
|
|
53
|
+
"""Async callable that expands a Master Skill node's ``skills_config``
|
|
54
|
+
into a list of per-skill entries (the same shape ``_append_skill_entries``
|
|
55
|
+
produces for non-master skills). Signature:
|
|
56
|
+
``(source_node_id, skills_config) -> list[entry]``."""
|
|
57
|
+
|
|
58
|
+
_MASTER_SKILL_EXPANDER: Optional[MasterSkillExpander] = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def register_master_skill_expander(fn: MasterSkillExpander) -> None:
|
|
62
|
+
"""Publish the Master-Skill expander callback.
|
|
63
|
+
|
|
64
|
+
Idempotent on equality. The :mod:`nodes.skill` plugin package
|
|
65
|
+
registers its expander from ``__init__.py`` on package import.
|
|
66
|
+
Edge-walking code calls :func:`get_master_skill_expander` and runs
|
|
67
|
+
whatever's registered; the framework has no skill_loader coupling.
|
|
68
|
+
"""
|
|
69
|
+
global _MASTER_SKILL_EXPANDER
|
|
70
|
+
if _MASTER_SKILL_EXPANDER is not None and _MASTER_SKILL_EXPANDER != fn:
|
|
71
|
+
raise ValueError("register_master_skill_expander: callback already registered " "by a different callable; refusing to overwrite")
|
|
72
|
+
_MASTER_SKILL_EXPANDER = fn
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_master_skill_expander() -> Optional[MasterSkillExpander]:
|
|
76
|
+
"""Return the registered expander callback, or None when no skill
|
|
77
|
+
plugin has wired it (in which case Master-Skill nodes silently
|
|
78
|
+
expand to no entries -- the agent still runs without skills)."""
|
|
79
|
+
return _MASTER_SKILL_EXPANDER
|
|
80
|
+
|
|
81
|
+
|
|
39
82
|
async def collect_agent_connections(
|
|
40
83
|
node_id: str,
|
|
41
84
|
context: Dict[str, Any],
|
|
42
85
|
database: "Database",
|
|
43
86
|
log_prefix: str = "[Agent]",
|
|
44
87
|
) -> Tuple[
|
|
45
|
-
Optional[Dict[str, Any]],
|
|
46
|
-
List[Dict[str, Any]],
|
|
47
|
-
List[Dict[str, Any]],
|
|
48
|
-
Optional[Dict[str, Any]],
|
|
49
|
-
Optional[Dict[str, Any]],
|
|
88
|
+
Optional[Dict[str, Any]], # memory_data
|
|
89
|
+
List[Dict[str, Any]], # skill_data
|
|
90
|
+
List[Dict[str, Any]], # tool_data
|
|
91
|
+
Optional[Dict[str, Any]], # input_data
|
|
92
|
+
Optional[Dict[str, Any]], # task_data
|
|
50
93
|
]:
|
|
51
94
|
"""Walk edges targeting ``node_id`` and collect everything an agent
|
|
52
95
|
needs from its connected nodes.
|
|
@@ -80,10 +123,7 @@ async def collect_agent_connections(
|
|
|
80
123
|
edge_targets = set(e.get("target") for e in edges)
|
|
81
124
|
logger.debug(f"{log_prefix} All edge targets in graph: {edge_targets}")
|
|
82
125
|
for e in incoming_edges:
|
|
83
|
-
logger.debug(
|
|
84
|
-
f"{log_prefix} Edge: source={e.get('source')}, "
|
|
85
|
-
f"targetHandle={e.get('targetHandle')}"
|
|
86
|
-
)
|
|
126
|
+
logger.debug(f"{log_prefix} Edge: source={e.get('source')}, " f"targetHandle={e.get('targetHandle')}")
|
|
87
127
|
|
|
88
128
|
tool_incoming = [e for e in incoming_edges if e.get("targetHandle") == "input-tools"]
|
|
89
129
|
logger.info(f"{log_prefix} Tool edges (input-tools handle): {len(tool_incoming)}")
|
|
@@ -101,18 +141,30 @@ async def collect_agent_connections(
|
|
|
101
141
|
if target_handle == "input-memory":
|
|
102
142
|
if source_node.get("type") == "simpleMemory":
|
|
103
143
|
memory_data = await _build_memory_entry(
|
|
104
|
-
node_id,
|
|
144
|
+
node_id,
|
|
145
|
+
source_node_id,
|
|
146
|
+
database,
|
|
147
|
+
log_prefix,
|
|
105
148
|
)
|
|
106
149
|
|
|
107
150
|
elif target_handle == "input-skill":
|
|
108
151
|
await _append_skill_entries(
|
|
109
|
-
source_node,
|
|
152
|
+
source_node,
|
|
153
|
+
source_node_id,
|
|
154
|
+
database,
|
|
155
|
+
skill_data,
|
|
156
|
+
log_prefix,
|
|
110
157
|
)
|
|
111
158
|
|
|
112
159
|
elif target_handle == "input-tools":
|
|
113
160
|
await _append_tool_entry(
|
|
114
|
-
source_node,
|
|
115
|
-
|
|
161
|
+
source_node,
|
|
162
|
+
source_node_id,
|
|
163
|
+
edges,
|
|
164
|
+
nodes,
|
|
165
|
+
database,
|
|
166
|
+
tool_data,
|
|
167
|
+
log_prefix,
|
|
116
168
|
)
|
|
117
169
|
|
|
118
170
|
elif target_handle in ("input-main", "input-chat") or target_handle is None:
|
|
@@ -126,7 +178,10 @@ async def collect_agent_connections(
|
|
|
126
178
|
|
|
127
179
|
elif target_handle == "input-task":
|
|
128
180
|
task_data = await _resolve_task_payload(
|
|
129
|
-
source_node_id,
|
|
181
|
+
source_node_id,
|
|
182
|
+
source_node,
|
|
183
|
+
context,
|
|
184
|
+
log_prefix,
|
|
130
185
|
)
|
|
131
186
|
|
|
132
187
|
logger.info(
|
|
@@ -164,7 +219,8 @@ async def _build_memory_entry(
|
|
|
164
219
|
"session_id": session_id,
|
|
165
220
|
"window_size": int(memory_params.get("window_size", 10)),
|
|
166
221
|
"memory_content": memory_params.get(
|
|
167
|
-
"memory_content",
|
|
222
|
+
"memory_content",
|
|
223
|
+
"# Conversation History\n\n*No messages yet.*\n",
|
|
168
224
|
),
|
|
169
225
|
"long_term_enabled": memory_params.get("long_term_enabled", False),
|
|
170
226
|
"retrieval_count": int(memory_params.get("retrieval_count", 3)),
|
|
@@ -175,9 +231,10 @@ async def _build_memory_entry(
|
|
|
175
231
|
"last_session_id": memory_params.get("last_session_id"),
|
|
176
232
|
}
|
|
177
233
|
logger.info(
|
|
178
|
-
"%s Connected memory node: node=%s session=%s (auto=%s) "
|
|
179
|
-
|
|
180
|
-
|
|
234
|
+
"%s Connected memory node: node=%s session=%s (auto=%s) " "content_length=%d last_session_id=%s",
|
|
235
|
+
log_prefix,
|
|
236
|
+
memory_node_id,
|
|
237
|
+
session_id,
|
|
181
238
|
not configured_session or configured_session == "default",
|
|
182
239
|
len(entry["memory_content"]),
|
|
183
240
|
entry["last_session_id"],
|
|
@@ -196,45 +253,31 @@ async def _append_skill_entries(
|
|
|
196
253
|
skill_params = await database.get_node_parameters(source_node_id) or {}
|
|
197
254
|
|
|
198
255
|
if skill_type == "masterSkill":
|
|
199
|
-
|
|
256
|
+
expander = get_master_skill_expander()
|
|
257
|
+
if expander is None:
|
|
258
|
+
logger.warning(
|
|
259
|
+
f"{log_prefix} Master Skill node found but no expander "
|
|
260
|
+
"registered. Skipping expansion -- ensure nodes.skill "
|
|
261
|
+
"is on the import path."
|
|
262
|
+
)
|
|
263
|
+
return
|
|
200
264
|
|
|
201
265
|
skills_config = skill_params.get("skills_config", {})
|
|
202
266
|
logger.debug(f"{log_prefix} Master Skill found with {len(skills_config)} configured skills")
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
for
|
|
206
|
-
|
|
207
|
-
continue
|
|
208
|
-
instructions = skill_cfg.get("instructions", "")
|
|
209
|
-
if instructions:
|
|
210
|
-
logger.debug(f"{log_prefix} Using DB instructions for {skill_key}")
|
|
211
|
-
else:
|
|
212
|
-
try:
|
|
213
|
-
skill = skill_loader.load_skill(skill_key)
|
|
214
|
-
if skill:
|
|
215
|
-
instructions = skill.instructions
|
|
216
|
-
logger.debug(
|
|
217
|
-
f"{log_prefix} Fallback: loaded instructions from skill folder for {skill_key}"
|
|
218
|
-
)
|
|
219
|
-
except Exception as e:
|
|
220
|
-
logger.warning(f"{log_prefix} Failed to load skill {skill_key}: {e}")
|
|
221
|
-
|
|
222
|
-
skill_data.append({
|
|
223
|
-
"node_id": f"{source_node_id}_{skill_key}",
|
|
224
|
-
"node_type": "masterSkill",
|
|
225
|
-
"skill_name": skill_key,
|
|
226
|
-
"parameters": {"instructions": instructions, "skillName": skill_key},
|
|
227
|
-
"label": skill_key,
|
|
228
|
-
})
|
|
229
|
-
logger.debug(f"{log_prefix} Master Skill enabled: {skill_key}")
|
|
267
|
+
entries = await expander(source_node_id, skills_config)
|
|
268
|
+
skill_data.extend(entries)
|
|
269
|
+
for entry in entries:
|
|
270
|
+
logger.debug(f"{log_prefix} Master Skill enabled: {entry['skill_name']}")
|
|
230
271
|
else:
|
|
231
|
-
skill_data.append(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
272
|
+
skill_data.append(
|
|
273
|
+
{
|
|
274
|
+
"node_id": source_node_id,
|
|
275
|
+
"node_type": skill_type,
|
|
276
|
+
"skill_name": skill_params.get("skill_name", skill_type),
|
|
277
|
+
"parameters": skill_params,
|
|
278
|
+
"label": source_node.get("data", {}).get("label", skill_type),
|
|
279
|
+
}
|
|
280
|
+
)
|
|
238
281
|
logger.debug(f"{log_prefix} Connected skill: {skill_type}")
|
|
239
282
|
|
|
240
283
|
|
|
@@ -265,27 +308,23 @@ async def _append_tool_entry(
|
|
|
265
308
|
continue
|
|
266
309
|
service_target_handle = service_edge.get("targetHandle")
|
|
267
310
|
if service_target_handle is not None and service_target_handle != "input-main":
|
|
268
|
-
logger.debug(
|
|
269
|
-
f"{log_prefix} Android Toolkit: Skipping edge with "
|
|
270
|
-
f"targetHandle: {service_target_handle}"
|
|
271
|
-
)
|
|
311
|
+
logger.debug(f"{log_prefix} Android Toolkit: Skipping edge with " f"targetHandle: {service_target_handle}")
|
|
272
312
|
continue
|
|
273
313
|
android_node_id = service_edge.get("source")
|
|
274
314
|
android_node = next((n for n in nodes if n.get("id") == android_node_id), None)
|
|
275
315
|
if android_node and android_node.get("type") in ANDROID_SERVICE_NODE_TYPES:
|
|
276
316
|
android_params = await database.get_node_parameters(android_node_id) or {}
|
|
277
|
-
connected_services.append(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
f"{log_prefix} Android toolkit connected service: "
|
|
287
|
-
f"{android_params.get('service_id')}"
|
|
317
|
+
connected_services.append(
|
|
318
|
+
{
|
|
319
|
+
"node_id": android_node_id,
|
|
320
|
+
"node_type": android_node.get("type"),
|
|
321
|
+
"service_id": android_params.get("service_id"),
|
|
322
|
+
"action": android_params.get("action"),
|
|
323
|
+
"parameters": android_params,
|
|
324
|
+
"label": android_node.get("data", {}).get("label", android_node.get("type")),
|
|
325
|
+
}
|
|
288
326
|
)
|
|
327
|
+
logger.debug(f"{log_prefix} Android toolkit connected service: " f"{android_params.get('service_id')}")
|
|
289
328
|
tool_entry["connected_services"] = connected_services
|
|
290
329
|
logger.debug(f"{log_prefix} Android toolkit has {len(connected_services)} connected services")
|
|
291
330
|
|
|
@@ -309,8 +348,7 @@ async def _append_tool_entry(
|
|
|
309
348
|
child_tool_id = child_edge.get("source")
|
|
310
349
|
child_tool_node = next((n for n in nodes if n.get("id") == child_tool_id), None)
|
|
311
350
|
logger.debug(
|
|
312
|
-
f"{log_prefix} Child agent {source_node_id}: "
|
|
313
|
-
f"tool edge from {child_tool_id}, node found: {child_tool_node is not None}"
|
|
351
|
+
f"{log_prefix} Child agent {source_node_id}: " f"tool edge from {child_tool_id}, node found: {child_tool_node is not None}"
|
|
314
352
|
)
|
|
315
353
|
if child_tool_node:
|
|
316
354
|
child_tool_type = child_tool_node.get("type", "")
|
|
@@ -318,10 +356,7 @@ async def _append_tool_entry(
|
|
|
318
356
|
child_tools.append({"node_type": child_tool_type, "label": child_tool_label})
|
|
319
357
|
if child_tools:
|
|
320
358
|
tool_entry["child_tools"] = child_tools
|
|
321
|
-
logger.debug(
|
|
322
|
-
f"{log_prefix} Child agent {source_node_id} has tools: "
|
|
323
|
-
f"{[t['label'] for t in child_tools]}"
|
|
324
|
-
)
|
|
359
|
+
logger.debug(f"{log_prefix} Child agent {source_node_id} has tools: " f"{[t['label'] for t in child_tools]}")
|
|
325
360
|
|
|
326
361
|
tool_data.append(tool_entry)
|
|
327
362
|
logger.debug(f"{log_prefix} Connected tool: {tool_type}")
|
|
@@ -336,9 +371,7 @@ async def _resolve_task_payload(
|
|
|
336
371
|
logger.info(f"{log_prefix} Found input-task edge from {source_node_id} (type={source_node.get('type')})")
|
|
337
372
|
|
|
338
373
|
source_output = context.get("outputs", {}).get(source_node_id)
|
|
339
|
-
logger.info(
|
|
340
|
-
f"{log_prefix} Context outputs check for {source_node_id}: {source_output is not None}"
|
|
341
|
-
)
|
|
374
|
+
logger.info(f"{log_prefix} Context outputs check for {source_node_id}: {source_output is not None}")
|
|
342
375
|
|
|
343
376
|
if not source_output:
|
|
344
377
|
get_output_fn = context.get("get_output_fn")
|
|
@@ -350,9 +383,7 @@ async def _resolve_task_payload(
|
|
|
350
383
|
except Exception as e:
|
|
351
384
|
logger.warning(f"{log_prefix} Failed to get output from DB: {e}")
|
|
352
385
|
else:
|
|
353
|
-
logger.warning(
|
|
354
|
-
f"{log_prefix} No get_output_fn in context, cannot retrieve task output"
|
|
355
|
-
)
|
|
386
|
+
logger.warning(f"{log_prefix} No get_output_fn in context, cannot retrieve task output")
|
|
356
387
|
|
|
357
388
|
logger.info(
|
|
358
389
|
f"{log_prefix} Source output for {source_node_id}: {source_output is not None}, "
|
|
@@ -361,11 +392,7 @@ async def _resolve_task_payload(
|
|
|
361
392
|
if not source_output:
|
|
362
393
|
return None
|
|
363
394
|
|
|
364
|
-
if (
|
|
365
|
-
isinstance(source_output, dict)
|
|
366
|
-
and "result" in source_output
|
|
367
|
-
and isinstance(source_output.get("result"), dict)
|
|
368
|
-
):
|
|
395
|
+
if isinstance(source_output, dict) and "result" in source_output and isinstance(source_output.get("result"), dict):
|
|
369
396
|
task_data = source_output.get("result")
|
|
370
397
|
logger.info(f"{log_prefix} Extracted nested task_data from result key")
|
|
371
398
|
else:
|
|
@@ -385,7 +412,7 @@ async def collect_teammate_connections(
|
|
|
385
412
|
) -> List[Dict[str, Any]]:
|
|
386
413
|
"""Walk ``input-teammates`` edges and return connected agents.
|
|
387
414
|
|
|
388
|
-
Used by ``orchestrator_agent`` / ``ai_employee
|
|
415
|
+
Used by ``orchestrator_agent`` / ``ai_employee``.
|
|
389
416
|
"""
|
|
390
417
|
nodes = context.get("nodes", [])
|
|
391
418
|
edges = context.get("edges", [])
|
|
@@ -402,12 +429,14 @@ async def collect_teammate_connections(
|
|
|
402
429
|
if node_type not in AI_AGENT_TYPES:
|
|
403
430
|
continue
|
|
404
431
|
params = await database.get_node_parameters(source_id) or {}
|
|
405
|
-
teammates.append(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
432
|
+
teammates.append(
|
|
433
|
+
{
|
|
434
|
+
"node_id": source_id,
|
|
435
|
+
"node_type": node_type,
|
|
436
|
+
"label": source_node.get("data", {}).get("label", node_type),
|
|
437
|
+
"parameters": params,
|
|
438
|
+
}
|
|
439
|
+
)
|
|
411
440
|
logger.debug(f"[Teams] Found teammate: {node_type} ({source_id})")
|
|
412
441
|
|
|
413
442
|
return teammates
|
|
@@ -447,10 +476,4 @@ def format_task_context(task_data: Dict[str, Any]) -> str:
|
|
|
447
476
|
"Report this error to the user and suggest next steps if appropriate."
|
|
448
477
|
)
|
|
449
478
|
|
|
450
|
-
return
|
|
451
|
-
f"Task update received:\n"
|
|
452
|
-
f"- Agent: {agent_name}\n"
|
|
453
|
-
f"- Task ID: {task_id}\n"
|
|
454
|
-
f"- Status: {status}\n"
|
|
455
|
-
f"- Data: {task_data}"
|
|
456
|
-
)
|
|
479
|
+
return f"Task update received:\n" f"- Agent: {agent_name}\n" f"- Task ID: {task_id}\n" f"- Status: {status}\n" f"- Data: {task_data}"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Shape validation for plugin identifiers used in URL routes + lookups.
|
|
2
|
+
|
|
3
|
+
Plugin node types (and credential provider ids) follow Python-identifier
|
|
4
|
+
rules: a letter or underscore followed by word characters. URL-facing
|
|
5
|
+
routes (``GET /api/schemas/nodes/{node_type}/icon`` etc.) and internal
|
|
6
|
+
helpers (``nodes._visuals.get_plugin_icon_path`` etc.) both validate
|
|
7
|
+
against this constraint to reject path-traversal injections at the
|
|
8
|
+
boundary.
|
|
9
|
+
|
|
10
|
+
Why this lives here (not inline at each callsite):
|
|
11
|
+
|
|
12
|
+
- One source of truth for the regex pattern. FastAPI consumes the raw
|
|
13
|
+
pattern string via ``Path(pattern=...)``; Python code consumes the
|
|
14
|
+
pre-compiled validator via :func:`is_valid_node_type`. Keeping the
|
|
15
|
+
pattern in one module guarantees the route-level and function-level
|
|
16
|
+
checks stay in lockstep.
|
|
17
|
+
- Recognised by CodeQL's ``py/path-injection`` taint analysis as a
|
|
18
|
+
sanitizer — the canonical "regex fullmatch on user input" pattern
|
|
19
|
+
(see https://codeql.github.com/codeql-query-help/python/py-path-injection/).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import re
|
|
25
|
+
from typing import Final
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Letter/underscore then word chars — the shape every registered
|
|
29
|
+
# ``BaseNode.type`` and ``Credential.id`` follows. Exposed as a raw
|
|
30
|
+
# string so FastAPI's ``Path(pattern=...)`` / ``Query(pattern=...)``
|
|
31
|
+
# can consume it directly.
|
|
32
|
+
NODE_TYPE_PATTERN: Final[str] = r"^[A-Za-z_][A-Za-z0-9_]*$"
|
|
33
|
+
|
|
34
|
+
_NODE_TYPE_RE: Final[re.Pattern[str]] = re.compile(NODE_TYPE_PATTERN)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def is_valid_node_type(value: str) -> bool:
|
|
38
|
+
"""True if ``value`` matches :data:`NODE_TYPE_PATTERN`.
|
|
39
|
+
|
|
40
|
+
Treats ``None`` / non-string input as invalid (defensive — the
|
|
41
|
+
type hint is ``str`` but callers may forward URL params verbatim).
|
|
42
|
+
"""
|
|
43
|
+
if not isinstance(value, str):
|
|
44
|
+
return False
|
|
45
|
+
return bool(_NODE_TYPE_RE.fullmatch(value))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = ["NODE_TYPE_PATTERN", "is_valid_node_type"]
|